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

fire sourcedata when source metadata and tiles are loaded #4347

Merged
merged 8 commits into from
Mar 8, 2017

Conversation

mollymerp
Copy link
Contributor

This PR does a couple things in order to make the sourcedata event more useful for users:

  • for vector tile sources, we currently fire sourcedata when the tilejson loads, but that is not really useful for users, so this PR will fire the event when the source + all tiles in the current viewport are loaded
  • we used to call SourceCache#reload on the sourcedata event, but that was causing all vector tiles to be loaded twice on initial load of the source, and because this is only meant to reload geojson sources after setData and image/video/canvas sources after setCoordinates I created a new event (😬) to handle this called source.update

@lucaswoj
Copy link
Contributor

lucaswoj commented Mar 1, 2017

  • for vector tile sources, we currently fire sourcedata when the tilejson loads, but that is not really useful for users, so this PR will fire the event when the source + all tiles in the current viewport are loaded

I would like to advocate for sourcedata to be fired whenever a source changes or loads new data (not just when it finishes loading) on the basis that

  • it stays true to the heritage of the data event, indicating that some data has arrived
  • it would allow us to simplify the API by removing the tiledata event
  • it creates a clear path towards custom sources which are always updating (for example due to animations or real time data) or untiled
  • it avoids stepping on the toes of dataend (perhaps we need a sourcedataend event)
  • we used to call SourceCache#reload on the sourcedata event, but that was causing all vector tiles to be loaded twice on initial load of the source, and because this is only meant to reload geojson sources after setData and image/video/canvas sources after setCoordinates I created a new event (😬) to handle this called source.update

Can we eliminate the need for this new event by calling SourceCache#reload directly within setData and setCoordinates?

@anandthakker
Copy link
Contributor

Can we eliminate the need for this new event by calling SourceCache#reload directly within setData and setCoordinates?

@lucaswoj as of now, sources are not 'aware of' / provided a reference to their containing SourceCache. We could either refactor to add this dependency now, or temporarily keep the (private) source.update event and wait for the upcoming SourceCache refactor.

@@ -115,7 +114,8 @@ class ImageSource extends Evented {
centerCoord.column = Math.round(centerCoord.column);
centerCoord.row = Math.round(centerCoord.row);

this.minzoom = this.maxzoom = centerCoord.zoom;
// TODO this is making tests fail... what is it for?
// this.minzoom = this.maxzoom = centerCoord.zoom;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lbud would love to know what this line is for. for some reason it's behaving in unexpected ways with the new eventing system and making the canvas source tests fail 😭

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lbud might be able to say more, but my recollection from a while back is that this had to do with 'tricking' the source cache into always requesting the 'right' tile from the ImageSource.

Copy link
Contributor

@lbud lbud Mar 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think @anandthakker is right — because of #3186 (👺) we have to trick the source cache into thinking there's only one appropriate tile to ever request for image + image-inheriting sources. 👀 …

@@ -475,6 +477,7 @@ class SourceCache extends Evented {
clearTimeout(this._timers[id]);
this._timers[id] = undefined;
}
// TODO should we keep this??
this._source.fire('data', { tile: tile, coord: tile.coord, dataType: 'tile' });
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lucaswoj @anandthakker do you think we need to fire an event when a tile is removed now that tiledata is ⚰️? I feel like this isn't useful, but let me know if you disagree.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this event isn't useful -- if we're not using it anywhere internally, then I'm all for removing it.

What might be useful someday in the future would be a way for users to listen for changes to the list of currently renderable/rendered tile coordinates -- but this doesn't really provide that, and anyway that's a super non-urgent nice-to-have.

@mollymerp
Copy link
Contributor Author

In its current state this PR:

  • removes source.load and replaces it with a sourcedata with a metadata property on the MapEventData
  • removes tiledata and replaces it with a sourcedata event with tile information in the MapDataEvent
  • adds an update property on the event data that triggers a reload of the tiles for use with mutable sources (eg GeoJSONSource#setData and ImageSource#setCoordinates) in order to prevent double loading of sources when they're initially added to the map

TODO:

  • figure out whats going on with the canvas source + tests
  • figure out what to do with the remaining vestiges of the tiledata event (on removeTile and redoPlacement)

cc @lucaswoj @anandthakker @lbud


// for sources with mutable data, this event fires when the underlying data
// to a source is changed. (i.e. GeoJSONSource#setData and ImageSource#serCoordinates)
if (this._sourceLoaded && e.dataType==="source" && e.update) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great ❤️

Notes/questions:

  1. We should update the internal documentation here to reflect these changes to the Source interface contract.

  2. Is e.metadata too generic a name? I'm not even sure if I like this idea yet, but what about something like e.sourceDataType == 'metadata' and e.sourceDataType == 'update'? (Basically having sourceDataType be analogous to dataType, but specific to source data events.)

  3. Is there still a possibility of a race condition here for vector sources? We need SourceCache#update() to be called after the TileJSON has loaded. This currently happens either here (for events with e.update) or via Style#_updateSources(). I'm imagining this problematic timeline:

  • map.addSource('mysource', ...) (plus map.addLayer() using mysource) => requests TileJSON, marks map._sourceDirty.
  • Next render frame fires; map._sourcesDirty being true leads to style._updateSources()... but since the TileJSON is still in flight, we don't get a SourceCache#update() for mysource.
  • TileJSON lands => VectorTileSource fires data with e.metadata => mysource's SourceCache marks itself _sourceLoaded.
  • But now, the actual tiles for mysource are not loaded until something else causes _sourcesDirty flag to be set.

Copy link
Contributor

@anandthakker anandthakker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Couple of internal-docs-related comments. Other than that, I'll just vote one last time for replacing 'update' with 'content', as that feels like it's clearer / more parallel with 'metadata'. Approving anyway though so your call 😄

@@ -50,7 +50,7 @@ exports.setType = function (name, type) {
* @param {string} options.type The source type, matching the value of `name` used in {@link Style#addSourceType}.
* @param {Dispatcher} dispatcher A {@link Dispatcher} instance, which can be used to send messages to the workers.
*
* @fires load to indicate source data has been loaded, so that it's okay to call `loadTile`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small addition: "@fires data with sourceDataType: 'metadata' to indicate..." -- since that's what source cache now requires of the event fired by Source.

// `update` is included here to prevent a race condition where `Style#_updateSources` is called
// before the TileJSON arrives. this makes sure the tiles needed are loaded once TileJSON arrives
// ref: https://github.com/mapbox/mapbox-gl-js/pull/4347#discussion_r104418088
this.fire('data', {dataType: 'source', sourceDataType: 'metadata'});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case (GeoJSONSource), the term 'metadata' is slightly odd, since there isn't really any metadata for geojson sources. As such, it might be more helpful to replace the comment about update in favor of a comment reminding us that we're firing metadata to notify the source cache that it's 👍 to ask us for tiles.

(I.e., reading this source, I think it's metadata that might be confusing; reading the vector/raster sources, it's update that might be confusing)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh good catch! this should be in vector_tile_source and not here.

source.on('data', t.end);
source.on('data', (e)=>{
if (e.sourceDataType === 'metadata') t.end();
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this test be checking for the 'update' (or 'content') event, rather than 'metadata'?

@mollymerp mollymerp force-pushed the source-event branch 2 times, most recently from c4ca6f4 to 41157c5 Compare March 8, 2017 02:28
@mollymerp
Copy link
Contributor Author

benchmark master 5fa31ef source-event 94c35d6
map-load 450 ms 86 ms
style-load 120 ms 116 ms
buffer 900 ms 899 ms
fps 60 fps 60 fps
frame-duration 4.2 ms, 0% > 16ms 4.3 ms, 0% > 16ms
query-point 0.71 ms 0.74 ms
query-box 57.15 ms 56.11 ms
geojson-setdata-small 5 ms 1 ms
geojson-setdata-large 92 ms 93 ms

🕐 benchmarks for posterity 🕙

@mollymerp mollymerp merged commit 77fc72b into master Mar 8, 2017
@mollymerp mollymerp deleted the source-event branch March 8, 2017 06:40
anandthakker pushed a commit that referenced this pull request Mar 8, 2017
* source sorcery ✨

* fix render tests 🎨

* fix unit tests 🔧

* refactor to make everything dependent on 'data' event with new properties

* change language, updates tests

* use sourceDataType event property 📆

* fixy tests ✅

* 'update' 👉 'content', update tests, docs 📝
@mollymerp mollymerp mentioned this pull request May 9, 2017
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants