Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add data, datastart and dataend events #1715

Open
scothis opened this issue Nov 13, 2015 · 50 comments
Open

Add data, datastart and dataend events #1715

scothis opened this issue Nov 13, 2015 · 50 comments

Comments

@scothis
Copy link
Contributor

scothis commented Nov 13, 2015

There are events for when individual tiles load, but it's difficult to know when all the tiles for a source have finished loaded. Essentially, I'd like an API to know if tiles requests are in flight, or if the all the data I would expect to view for a given view is already loaded. Firing an event when the state transitions would also be helpful.

Knowing when a source is fully loaded is useful for smoothly transitioning between different overlay sources without a flash as tiles load.

As a quick sketch, I'd imaging something like:

map.on('source.loading', function (e) {
  e.status; // one of 'fetching', 'idle', 'errored'
});
@jfirebaugh
Copy link
Contributor

@anandthakker
Copy link
Contributor

@jfirebaugh I think Map.load doesn't account for view changes requiring new tiles to be downloaded?

@scothis A related issue I encountered today (copying from a #sneak-peek slack conversation):

I’m trying to use featuresIn on load, and i think i’m running into a race. I am waiting until i see source.load events for the sources that i need features from, but even though source.load has fired, sometimes some of the individual ​_tiles_​ aren’t loaded. I just tried a workaround where, if !source.loaded(), wait for tile.load and check again, but the problem is that if there’s a missing tile, source.loaded() never becomes true. I’m thinking about hacking this in by setting an errored flag here: https://github.com/mapbox/mapbox-gl-js/blob/master/js/source/vector_tile_source.js#L77
and then having tile_pyramid.loaded check for loaded || errored instead of just loaded.

Just tried that change here: master...anandthakker:fix-source-loaded. Seems to resolve my particular issue; lemme know if you want a pr, though maybe you'd rather solve this more systematically.

@anandthakker
Copy link
Contributor

^ Hmm, okay so my hack doesn't really resolve the problem after all: even after correcting pyramid.loaded() and then waiting for it, I end up with missing data in my featuresIn request. My guess is that loaded() comes back true when all the tiles that have been requested have come back, but there more yet that need to be requested for the current view.

I guess this is my 👍 for something like what @scothis proposed.

@scothis
Copy link
Contributor Author

scothis commented Nov 13, 2015

@jfirebaugh looking for something like the load event that stays up to date, and doesn't only fire once.

@jfirebaugh
Copy link
Contributor

Got it, thanks. I think maybe we should make load fire whenever the view quiesces. This would be a breaking change and we'd need to switch most examples to map.once('load', ...).

@derrickpelletier
Copy link

Just wanted to +1 that I'm looking for something similar for when a geojson-source data url has loaded. Which I suppose would inherently trigger tile-loaded events. Just a slightly different case than a regular tile source.

@lucaswoj
Copy link
Contributor

lucaswoj commented Dec 4, 2015

I'm in favor of building this and calling the event something other than load.

Some strawman proposals:

  • quiesce
  • coalesce
  • stabilize

@tmcw
Copy link
Contributor

tmcw commented Dec 4, 2015

complete?

@DannyDelott
Copy link
Contributor

What about updating the function signatures of map#addSource to take accept an optional callback as the last argument?

 map.addSource(id, source, function(){ 
   // source loaded, do stuff...
});

@mourner
Copy link
Member

mourner commented Dec 7, 2015

Leaflet uses load for this, although I agree that it's a bit ambiguous. complete sounds fine.

@tristen
Copy link
Member

tristen commented Dec 10, 2015

+1. Related to #1715 (comment) I would expect source.load to fire twice in this scenario and sometimes there's a long lag between the geojson layer finally added to the map:

source load

@timjcook
Copy link

I have successfully been able to detect when my features have loaded by watching the render event that the map fires when a single style is rendered and each time checking map.loaded(). I can then know it is ok to call featuresIn() and I will get my features.

map.on("render", function() {
  if(map.loaded()) {
    map.featuresIn(...);
  }
});

Ideally, an event at the exact time all layers on the map are ready is better, rather than checking all the render events but this is getting me by. Looking forward to the update.

@DannyDelott
Copy link
Contributor

+1 @timjcook

@ryanbaumann
Copy link
Contributor

+1 @timjcook that worked for use case as well, adding a loading bar while source.setData() is executing.

@lucaswoj
Copy link
Contributor

I'm thinking about resolving this issue with

  • Mapⓔdata: a resource (tile, sprite, glyph) has been loaded OR changed with GeoJSONSource#setData
  • Mapⓔdataend: no future data events will be fired (unless map is interacted with)

This parallels the structure of

  • Mapⓔmove: the map's viewport has been moved
  • Mapⓔmoveend: no future move events will be fired (unless map is interacted with)

💭 @scothis?

@scothis
Copy link
Contributor Author

scothis commented Apr 12, 2016

@lucaswoj works for me

@jfirebaugh
Copy link
Contributor

👍

I like the symmetry there.

@lucaswoj lucaswoj changed the title Indicate when all tiles are loaded for a view Add datastart and dataend events Apr 14, 2016
@lucaswoj lucaswoj removed their assignment Jan 10, 2017
@chriswhong
Copy link
Contributor

chriswhong commented Jan 16, 2017

+1, I would like to implement a loading spinner on my map when it is fetching tiles or waiting for geojson ajax calls to return. I would love it if there was a simple event that would fire when all layers are fully rendered.

@LDF913
Copy link

LDF913 commented Feb 2, 2017

is it possible to load different data layers dependent and a time of day month. ex. broadcast listener of system time

@lucaswoj
Copy link
Contributor

lucaswoj commented Feb 2, 2017

@LDF913 Due to the large volume of issues we get and the relatively small size of our team, we are unable to provide support here on GitHub. I recommend asking this question on Stack Overflow.

@jdeboer-geoplan
Copy link

A reliable dataend event would be great, using render and map.loaded() occasionally doesn't work, on the final render event fire the map is still not loaded. Can't reliably reproduce though.

@joehand
Copy link

joehand commented Aug 15, 2017

+1, I would like to implement a loading spinner on my map when it is fetching tiles or waiting for geojson ajax calls to return. I would love it if there was a simple event that would fire when all layers are fully rendered.

I was able to fairly reliably check if ajax-type data was loaded via:

 map.on('data', function (data) {
    if (data.dataType === 'source' && data.isSourceLoaded) {
      console.log('data loaded', data)
      // stop listening to map.on('data'), if applicable
    }
  })

The map.on('data') event is pretty noisy though.

@jucor
Copy link

jucor commented Oct 11, 2017

@joehand: thanks for map.on('data') , it sounded great at first :) Sadly, it is so noisy that it misses many events, and as such my spinning wheel (same use case as @chriswhong ) often stays stuck, which makes for very poor user experience. Can't wait for this feature!

@jdeboer-geoplan
Copy link

@jucor what I've done is to just start a timer on sourcedata event that checks map.loaded() every half a second and then removes the loading wheel. Not had a problem with this approach

@jucor
Copy link

jucor commented Oct 12, 2017

@jdeboer-geoplan Nice workaround, thanks!

@wshaver
Copy link

wshaver commented Oct 16, 2017

Following this thread as my app also needs this.

@kylebebak
Copy link

It's amazing that this issue is 2 years old. This feature is table stakes.

@sebastianovide
Copy link

is this work in progress ?

@ericjames
Copy link

Is this related to #3964

I'd like to see it as on complete as in, no more pending source requests

@strech345
Copy link

strech345 commented Jul 25, 2018

i don't understand how for example get the event tile.remove. Was it removed, is there a documentation about all events?

i use this.map.on('data', event...
with event.tile.uses i check if it's loaded or unloaded. But there are much events missing of unloaded tiles. I compare to map.style.sourceCaches.xxxxx._tiles. This looks correct but i dont get for all unloaded tiles a event.

@dollysingh3192
Copy link

@jfirebaugh I think Map.load doesn't account for view changes requiring new tiles to be downloaded?

@scothis A related issue I encountered today (copying from a #sneak-peek slack conversation):

I’m trying to use featuresIn on load, and i think i’m running into a race. I am waiting until i see source.load events for the sources that i need features from, but even though source.load has fired, sometimes some of the individual tiles aren’t loaded. I just tried a workaround where, if !source.loaded(), wait for tile.load and check again, but the problem is that if there’s a missing tile, source.loaded() never becomes true. I’m thinking about hacking this in by setting an errored flag here: https://github.com/mapbox/mapbox-gl-js/blob/master/js/source/vector_tile_source.js#L77
and then having tile_pyramid.loaded check for loaded || errored instead of just loaded.

Just tried that change here: master...anandthakker:fix-source-loaded. Seems to resolve my particular issue; lemme know if you want a pr, though maybe you'd rather solve this more systematically.

Hi @anandthakker,

I am facing the same issue of "sourcedata" callback is not calling if one of the tiles in viewport fails to load, even the map.loaded() is not true.

I have high dependency on this, I set the state of feature on this basis(querySourceFeatures). I am stuck in this, if there is an alternative to this. Please suggest.

Like if we can abort/unload/remove that particular tile and make e.isSourceLoaded to true in callback

if (e.isSourceLoaded && e.sourceId === "src_chairs") {
//code to execute
}

Also, attaching screenshot
screen shot 2018-12-03 at 10 25 27 am

Please give me some suggestions on this.

@hjrobinson
Copy link

hjrobinson commented Apr 30, 2019

I appreciate jdeboer-geoplan's suggestion; he or she could have been a little more explicit. I think a recursive timeout is the answer. This seems to work (most of the time) for me. I'm not sure if areTilesLoaded() helps or not. Also, isStyleLoaded could be added.

function checkOutThisLoad(){
    if (map.loaded() == true && map.areTilesLoaded() == true) {
         console.log("load is complete");
    } else {
       console.log("load is incomplete");
       setTimeout(function() {
           checkOutThisLoad();
       },1000);
    }
}

@Babeetlebum
Copy link

Babeetlebum commented Aug 19, 2020

Edit : I went for the idle event which does exactly what I want

My use case : I want to use queryRenderedFeatures after adding a layer.

It's quite similar to the airport example in the documentation. This issue was avoided by displaying a message Drag the map to populate results but I can't affort to do that. I need the list to be updated when the layer loads

If I understand it correctly, dataend should be the event I need, right ?

Seeing how often the render event is fired I went for a hack by removing the listener after some time after adding the layer

// when a new layer is added
layers$.subscribe((layers) => {
      this.map.on("render", checkRenderedFeatures);
      setTimeout(() => this.map.off("render", checkRenderedFeatures), 2000);
});

This way checkRenderedFeatures is called ~30-50 times before the listener is removed

I don't like it one bit but hopefuly dataend will solve this issue for me. I'm fully aware that my "temporary hack" comment will still be in the codebase in 10 years :D

@hjrobinson
Copy link

hjrobinson commented Aug 19, 2020

Yes, idle provided a solution for me too. I don't know how many years the idle event has been available but it should have been mentioned a long time ago for this issue. Like anyone else I guess I could have said something earlier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.