AzureMapsControl.Components icon indicating copy to clipboard operation
AzureMapsControl.Components copied to clipboard

Add support for adding IconSprites for symbol layers & clustering

Open LeviateK opened this issue 3 years ago • 7 comments

Hi Arnaud - Similar to your Symbol example where it is tied to a datasource, I have a need to add custom imageSprites so the data points can be easily assigned. I have a solution in use with 1 custom image sprite using the standard javascript control, but would much prefer to use this package for it's simplicity if this feature were to exist. The ImageLayer requiring 4 points does not work, as my data is LAT/LON.

azure-maps-control reference: https://docs.microsoft.com/en-us/javascript/api/azure-maps-control/atlas.imagespritemanager?view=azure-maps-typescript-latest

Sample Custom Icon javascript located here: https://docs.microsoft.com/en-us/azure/azure-maps/map-add-pin

LeviateK avatar Apr 13 '22 15:04 LeviateK

Hi @LeviateK, sorry for the delay. It doesn't look like much to do, I'll try to take a look as soon as I find time to work on this.

arnaudleclerc avatar Apr 20 '22 07:04 arnaudleclerc

Thanks @arnaudleclerc - appreciate you taking a look at it. I've used it with the native javascript with 1 custom icon, but struggle to get other icons for different data sets. My full map design would then include a popup per symbol with a link to a details page.

LeviateK avatar Apr 20 '22 13:04 LeviateK

This will have to be delayed a little bit more, sorry for that, but I didn't forget about it. I cannot find any time to work on this currently. I will let you know as soon as any progress is made

arnaudleclerc avatar May 13 '22 15:05 arnaudleclerc

No worries mate, I've got part of it working with the native JavaScript, and am at least 6 weeks out from needed this.

LeviateK avatar May 13 '22 19:05 LeviateK

In desperate need of this feature as well...

mitikov avatar Jul 08 '22 13:07 mitikov

@LeviateK could you share me the solution you used in native Javascript for this? I really need to get this fixed too

arnvanhoutte avatar Sep 30 '22 21:09 arnvanhoutte

@arnvanhoutte Absolutely - I am far from a Javascript expert and constantly ran into syntax issues, but this is what I came up with based on official documentation and some samples. This is in a ASP.NET Core project with Razor pages, so the cshtml.cs OnLoad gets my locations with EntityType as the differentiator passed through via ViewBag, which are then mapped to the iconSprites. I also used clustering, reducing initial data points on load. Let me know if you have any questions.

`

                var map, dataSource, popup;

                //Note that the typeahead parameter is set to true.

                var geocodeServiceUrlTemplate = 'https://atlas.microsoft.com/search/{searchType}/json?typeahead=true&subscription-key={subscription-key}&api-version=1&query={query}&language={language}&lon={lon}&lat={lat}';
                //URL to fetch Access token
                var url = 'https://adtokens.azurewebsites.net/api/HttpTrigger1?code=dv9Xz4tZQthdufbocOV9RLaaUhQoegXQJSeQQckm6DZyG/1ymppSoQ==';

                //Define an HTML template for a custom popup content laypout.
                var popupTemplate = '<div class="customInfobox"><div class="name">{name}</div>{description}</div>';

                function GetMap()
                {

                    //Initialize a map instance.

                    map = new atlas.Map('myMap', {

                        //Add your Azure Maps subscription key to the map SDK. Get an Azure Maps key at https://azure.com/maps
                        center: [-33.33, 35.641],
                        zoom: 1.5,
                        style: 'grayscale_dark',
                        authOptions: {

                            authType: 'subscriptionKey',
                            subscriptionKey: '#YourKeyHere',
                            getToken: function (resolve, reject, map) {
                                fetch(url).then(function (response) {
                                    return response.text();
                                }).then(function (token) {
                                    resolve(token);
                                });
                            }
                        }

                    });

                    //Wait until the map resources are ready.
                    // Add all the icons -- wwwroot/images
                    map.events.add('ready', function () {
                        // Build array of custom icons
                        var iconPromises = [
                            map.imageSprite.add('icon1', '../images/image1.png'), 
                            map.imageSprite.add('icon2', '../images/image2.png')
                            // Add more icons here if necessary
                        ];
                        Promise.all(iconPromises).then(function ()
                        {

                        // Map Controls Section
                        /* Construct and add a compass control*/
                        var compassControl = new atlas.control.CompassControl();
                        map.controls.add(compassControl, {
                            position: "bottom-right"
                        });

                        /* Construct and add a zoom control*/
                        var zoomControl = new atlas.control.ZoomControl();
                        map.controls.add(zoomControl, {
                            position: "bottom-right"
                        });

                        /* Construct and add a pitch control*/
                        var pitchControl = new atlas.control.PitchControl();
                        map.controls.add(pitchControl, {
                            position: "bottom-right"
                        });

                        /* Construct and add a style control*/
                        var styleControl = new atlas.control.StyleControl();
                        map.controls.add(styleControl, {
                            position: "bottom-right"
                        });
                        // End of Map Controls Section
                        // Add Data Source
                        
                            //Create a data source and add it to the map.
                            dataSource = new atlas.source.DataSource(null, {
                                cluster: true,
                                clusterRadius: 5,
                                clusterMaxZoom: 2
                            });
                            map.sources.add(dataSource);
                            // Pull from code-behind for project data
                           @{
        
                            List<Project.Models.MapData> perDiems = ViewBag.Locations;
                            var dLocations = perDiems.Distinct().ToList();
                            }

                            @for (int i = 0; i < dLocations.Count(); i++)
                            {
                              <text>
                                var point_@i = new atlas.data.Feature(new atlas.data.Point(['@dLocations[i].LONG', '@dLocations[i].LAT']),
                                    {
                                        Name: '@dLocations[i].City',
                                        Description: '@dLocations[i].State',
                                        EntityType: '@dLocations[i].EntityType'
                                    });
                                dataSource.add([point_@i]);

                                </text>
                            }

                            //Add Layer for clusters
                            // EntityType for determining which icon to use
                            var clusterLayer = new atlas.layer.SymbolLayer(dataSource, null, {
                                iconOptions: {
                                    image: 
                                    [                                           
                                        'match',
                                        ['get', 'EntityType'],

                                        '1','icon1',
                                        '2','icon2',
                                        'icon1'
                                    ],
                                    allowOverlap: true
                                },
                                textOptions: {
                                    textField: ['get', 'point_count_abbreviated'],
                                    offset: [-0.25, -1.65]
                                },
                                filter: ['has', 'point_count']
                            });
                            map.layers.add(clusterLayer);
                            // Add a layer for rendering point data as symbols.
                            // Create a layer to render point data.
                            var symbolLayer = new atlas.layer.SymbolLayer(dataSource, null, {
                                iconOptions: {
                                    image:
                                    [                               
                                        'match',
                                        ['get', 'EntityType'],

                                        '1','icon1',
                                        '2','icon2',
                                        'icon1'
                                    ],
                                    allowOverlap: true
                                    //ignorePlacement: true
                                },
                                 filter: ['!', ['has', 'point_count']]
                            });

                            map.layers.add(symbolLayer);

                            //Create a popup but leave it closed so we can update it and display it later.
                            popup = new atlas.Popup({
                                pixelOffset: [0, -50]
                            });

                            //Add a click event to the symbol layer.
                            map.events.add('click', symbolLayer, symbolClicked);
                        });

                        // Popup clicked shows location
                        function symbolClicked(e) {
                            //Make sure the event occured on a point feature.
                            if (e.shapes && e.shapes.length > 0) {
                                var content, coordinate;

                                //Check to see if the first value in the shapes array is a Point Shape.
                                if (e.shapes[0] instanceof atlas.Shape && e.shapes[0].getType() === 'Point') {
                                    var properties = e.shapes[0].getProperties();
                                    content = popupTemplate.replace(/{name}/g, properties.Name).replace(/{description}/g, properties.Description);
                                    coordinate = e.shapes[0].getCoordinates();
                                } else if (e.shapes[0].type === 'Feature' && e.shapes[0].geometry.type === 'Point') {

                                    //Check to see if the feature is a cluster.
                                    if (e.shapes[0].properties.cluster) {
                                        content = '<div style="padding:10px;">Cluster of ' + e.shapes[0].properties.point_count + ' symbols</div>';
                                    } else {
                                        //Feature is likely from a VectorTileSource.
                                        content = popupTemplate.replace(/{name}/g, properties.Name).replace(/{description}/g, properties.Description);
                                    }

                                    coordinate = e.shapes[0].geometry.coordinates;
                                }

                                if (content && coordinate) {
                                    //Populate the popupTemplate with data from the clicked point feature.
                                    popup.setOptions({
                                        //Update the content of the popup.
                                        content: content,

                                        //Update the position of the popup with the symbols coordinate.
                                        position: coordinate
                                    });

                                    //Open the popup.
                                    popup.open(map);
                                }
                            }
                        }
                    });
                    
                }

</script>`

To have it render, body tag looks like this: <body onload="GetMap()">

LeviateK avatar Oct 03 '22 15:10 LeviateK