Mapbox GL JS – Understanding the “Source Loaded” Event

javascriptmapboxmapbox-glmapbox-gl-js

I'm adding a vector tile source to my Mapbox GL js map like this:

    this.map.on('load', () => {
        this.map.addSource('userSource', {
            type: 'vector',
            url: 'mapbox://<my user source>',
        });

        this.map.addLayer({
            id: 'userDataLayer',
            type: 'fill',
            source: 'userSource',
            'source-layer': 'foobar_areas',
            paint: {
                'fill-outline-color': 'rgba(50,50,150,.4)',
                'fill-color': 'rgba(10,190,20,.2)',
            },
        });
    });

I want to initialize some other modules once userDataLayer is ready to go, but I don't know what event to listen for. I tried this:

this.map.on('data', (e) => { 
    const { dataType, sourceId, isSourceLoaded } = e; 
        if (dataType === 'source' && sourceId === 'userSource' && isSourceLoaded) {
        // ...

Except that fires many times… and then fires again when you apply filters, etc.

Is there an event that fires just when the source is loaded?

Best Answer

I was dealing with this again today, so here's an update!

As of mapbox-gl v0.44.2, it's still not quite reliable enough to listen for the sourcedata event and check the properties of the event parameter that gets passed to the callback, even though I think that's what you're supposed to do. You'd want to check sourceId === 'your-source-id', isSourceLoaded === true, and sourceDataType !== 'metadata'. If an event satisfied these 3 things, you could assume your source is loaded and ready to work with.

I've been experimenting with this technique, and I found that switching the source at runtime doesn't always fire a sourcedata event that satisfies these criteria. It's a real hassle.

The most solid solution I've been able to find so far is to listen for sourcedata events, but use map.isSourceLoaded every single time to check if your particular source is ready.

So:

function sourceCallback() {
    // assuming 'map' is defined globally, or you can use 'this'
    if (map.getSource('my-data') && map.isSourceLoaded('my-data')) {
        console.log('source loaded!');
    }
}

map.on('sourcedata', sourceCallback);
Related Question