1
votes

I'm trying to implement a single-layer in mapbox-GL that shows icons which change when clicked. So far I've attempted the following:

Use a property (after pre-loading the images as active and inactive):

'icon-image': ["case",["boolean", ["feature-state", "clicked"], false],
                                "inactive",
                                "active"
                            ],

And various versions of map.updateImage() to dynamically change the image that is displayed for each point:

map.updateImage(`point1`, map.loadImage('/img/pin_active.png'))
map.updateImage(`point1`, 'active')) //with the image being loaded beforehand as 'active'
map.updateImage(`point1`, map.loadImage('/img/pin_active.png', function(error, image) {
                        if (error) throw error;
                        map.addImage(point1, image);
                    }))

The only solution that does work is using SDF (mapbox gl change icon color) - but this does not work for my icons, which are multi-color (and they get pretty ugly since the SDF format seems to scale badly).

Any ideas on how to approach this?

1
What exactly do you mean, icons which change when clicked? If you're just cycling through a couple of icons, it would be much simpler to just load them all in advance. Or even use markers rather than icon layers.Steve Bennett
I just want to display an 'active' version of the icon when said Icon has been clicked. It would be fine to just cycle through a set of icons. I've tried to make the icon-image dependent on feature state, but it appears that the layout layer does not support this kind of reactive property?T. Altena

1 Answers

2
votes

Ok, so after a bit of extra fiddling around I found a working solution. Just leaving this here for whoever finds it later.

If we load the images beforehand as strings active and inactive:

            map.loadImage('/img/pin_blue.png', function(error, image) {
                if (error) throw error;
                map.addImage('inactive', image);
            })
            map.loadImage('/img/pin_red.png', function(error, image) {
                if (error) throw error;
                map.addImage('active', image);
            })

we can do the following:

            let data = {} // object that stores the geojson data
            let points = map.queryRenderedFeatures(e.point, {
                layers: ['projects-point']
            })
            if (points.length) {
                data.map(function(item, index) {
                if (item.id === points[0].id) {
                    data[index].properties.image = 'active'
                }
                else
                    data[index].properties.image = 'inactive'
            })
            map.getSource('projects').setData(this.dataObj.data)
            }