jbobxyz
jbobxyz

Reputation: 11

How do I update popups on Azure Maps when re-rendering my map?

I'm currently working on an internal CRM application for my company. Its built with Blazor, and utilizes the Azure Maps javascript SDK. I'm using Azure Maps to visualize Map Items collected by player's of my company's game. All of the map setup is done in javascript according to the documentation provided by Microsoft. The map has a symbol layer for displaying points, and a line layer for rendering the path traveled by a given player. Additionally, we are generating a summary of the player's collection stats (speed, distance, and time between collections). The page this component lives on has a DateRangePicker, a drop down menu of different item types, and a button to run the query. Currently, users of this web page can change the date range and the item type and rerun the query. This causes the Summary as well as the map with all points and the line to be re-rendered correctly. However, when the map is re-rendered, the popups attached to the points are no longer on the map, or are at least unresponsive to mouse events. If anyone has any ideas on how to keep and update my popups across map renderings, they would be greatly appreciated.

Upvotes: 0

Views: 1124

Answers (2)

rbrundritt
rbrundritt

Reputation: 17964

Glad to here you resolved your issue and posted the answer for others. One recommendation, when ever possible it is best to reuse the map instance and layers. You could run the code above once in your app, then in the future use datasource.setShapes and pass in the new points. This would automatically update the data on the map. If the popup is open when adding new data, simply close it. It also looks like some optimizations can be done around your point conversion code/speed calculations. Here is a modified version of your code that may provide some performance enhancements.

window.adminMap = (() => {

    let map;
    let datasource;
    let point;
    let popup;
    let lineLayer;

    const setupItemsMap = (subscriptionKey, mapItems) => {

        if (map != null) {
            map.dispose();
        }

        map = new atlas.Map('itemsMap', {
            center: [mapItems[0].coordinate.long, mapItems[0].coordinate.lat],
            centerOffest: [0, 0],
            zoom: 15,
            pitch: 90,
            style: 'road',
            language: 'en-US',
            view: 'Auto',
            authOptions: {
                authType: 'subscriptionKey',
                subscriptionKey: subscriptionKey
            }
        });

        map.events.add('ready', () => {

            //Create a data source and add it to the map.
            datasource = new atlas.source.DataSource(null, {
                lineMetrics: true
            });
            map.sources.add(datasource);
            
            lineLayer = new atlas.layer.LineLayer(datasource, null, {
                strokeWidth: 5,
                //strokeGradient: speedGradient
            });
            
            //Create a line layer and pass in a gradient expression for the strokeGradient property.
            map.layers.add(lineLayer);

            //Create a layer to render each data point along the path.
            var pointLayer = new atlas.layer.SymbolLayer(datasource, null, {
                //Only render point data in this layer, not the points of the line.
                filter: ['==', ['geometry-type'], 'Point']
            });

            //Create a popup. *This needs to update with the rest of the map!
            popup = new atlas.Popup();
            popup.open(map);

            map.layers.add(pointLayer);

            map.events.add('click', pointLayer, pointClicked);

            //Add mapItems to data source.
            setMapItems(mapItems);
        });
    }
    
    const setMapItems = (mapItems) => { 
        let points = [];        
        let previous;
        let point;
        
        for(let i = 0, len = mapItems.length; i < len; i++){
                    
            point = new atlas.data.Feature(new atlas.data.Point([mapItem.coordinate.long, mapItem.coordinate.lat]), {
                collectedAt: mapItem.collectedAt,
                speed: 0
            });
            
            if(previous){
                point.properties.speed = atlas.math.getSpeedFromFeatures(previous, point, "collectedAt", "milesPerHour", 1);
            }
            
            points.push(point);
            
            previous = point;
        }

        var line = createLineFrom(points);          
        
        //Calculate a color gradient expression based on the speed of each data point.
        lineLayer.setOptions({
            strokeGradient: calculateGradientExpression(points, line)
        });
        
        points.push(line);
        
        //Add data to data source using setShapes. This clears and adds the data and only triggers one re-render of the map.
        datasource.setShapes(points);
        
        if(popup){
            popup.close();
        }       
    }
}

Upvotes: 0

jbobxyz
jbobxyz

Reputation: 11

I was able to solve for this problem disposing of any map resources before attempting to create and render another map. This ensures that any html from previous map renders do not get in the way of the current map.

window.adminMap = (() => {

    let map;
    let datasource;
    let point;
    let popup;

    const setupItemsMap = (subscriptionKey, mapItems) => {

        if (map != null) {
            map.dispose();
        }

        map = new atlas.Map('itemsMap', {
            center: [mapItems[0].coordinate.long, mapItems[0].coordinate.lat],
            centerOffest: [0, 0],
            zoom: 15,
            pitch: 90,
            style: 'road',
            language: 'en-US',
            view: 'Auto',
            authOptions: {
                authType: 'subscriptionKey',
                subscriptionKey: subscriptionKey
            }
        });

        map.events.add('ready', () => {

            //Create a data source and add it to the map.
            datasource = new atlas.source.DataSource(null, {
                lineMetrics: true
            });
            map.sources.add(datasource);

            var points = [];
            mapItems.forEach(function (mapItem) {

                point = new atlas.data.Feature(new atlas.data.Point([mapItem.coordinate.long, mapItem.coordinate.lat]), {
                    collectedAt: mapItem.collectedAt,
                    speed: 0
                });
                points.push(point);
            });

            points.forEach(function (point) {

                let previous;
                let current = points.indexOf(point);

                if (current > 0) {
                    previous = current - 1;
                    point.properties.speed = atlas.math.getSpeedFromFeatures(points[previous], points[current], "collectedAt", "milesPerHour", 1);
                }
                else {
                    point.properties.speed = 0;
                }
            });
            datasource.add(points);

            var line = createLineFrom(points);
            datasource.add(line);

            //Calculate a color gradient expression based on the speed of each data point.
            var speedGradient = calculateGradientExpression(points, line);

            //Create a line layer and pass in a gradient expression for the strokeGradient property.
            map.layers.add(new atlas.layer.LineLayer(datasource, null, {
                strokeWidth: 5,
                strokeGradient: speedGradient
            }));

            //Create a layer to render each data point along the path.
            var pointLayer = new atlas.layer.SymbolLayer(datasource, null, {
                //Only render point data in this layer, not the points of the line.
                filter: ['==', ['geometry-type'], 'Point']
            });

            //Create a popup. *This needs to update with the rest of the map!
            popup = new atlas.Popup();
            popup.open(map);

            map.layers.add(pointLayer);

            map.events.add('click', pointLayer, pointClicked);
        });
    }

Upvotes: 1

Related Questions