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" event #3255

Merged
merged 21 commits into from
Sep 29, 2016
Merged

Add "data" event #3255

merged 21 commits into from
Sep 29, 2016

Conversation

lucaswoj
Copy link
Contributor

@lucaswoj lucaswoj commented Sep 26, 2016

This PR consolidates various undocumented data lifecycle events into the new data and dataloading events.

  • replace source.change with data
  • augment source.load with data
  • replace style.change with data
  • augment style.load with data
  • replace tile.add with dataloading
  • replace tile.load with data
  • replace tile.removewith data
  • replace source.add with data
  • replace source.remove with data
  • replace layer.add with data
  • replace layer.remove with data
  • document data event
  • document dataloading event
  • merge geoJSON, tileJSON, image, and video types into a source type
  • merge sprite into the style type

Having an event per resource type per possible mutation produced a complex taxonomy of events. Understanding, documenting, and using these events was difficult. Most users did not require the level of granularity exposed by this taxonomy. Some use cases required listening to many different events.

The unified data event is easy to understand, document, and use. Users who require more granularity may inspect the MapDataEvent object.

A MapDataEvent object is attached to the Map#data event.
The Map#data event is fired when any map data loads or changes. The types
of map data are:

  • 'geoJSON': GeoJSON data associated with a geojson source.
  • 'tileJSON': TileJSON metadata associated with a vector or raster source.
  • 'style': The style used by the map
  • 'sprite': The icons and patterns used by the style
  • 'image': A image source's image and coordinates
  • 'video': A video source's video and coordinates
  • 'tile': A vector or raster tile

cc @jfirebaugh @lbud @mollymerp @mapsam @mourner

Launch Checklist

  • briefly describe the changes in this PR
  • document any changes to public APIs
  • post benchmark scores
  • manually test the debug page

@lucaswoj
Copy link
Contributor Author

Benchmarks

map-load

master 24f2135: 119 ms
datastart 1ef4213: 105 ms

style-load

master 24f2135: 109 ms
datastart 1ef4213: 113 ms

buffer

master 24f2135: 961 ms
datastart 1ef4213: 941 ms

fps

master 24f2135: 60 fps
datastart 1ef4213: 60 fps

frame-duration

master 24f2135: 6.9 ms, 0% > 16ms
datastart 1ef4213: 7.2 ms, 1% > 16ms

query-point

master 24f2135: 1.04 ms
datastart 1ef4213: 1.12 ms

query-box

master 24f2135: 85.34 ms
datastart 1ef4213: 85.61 ms

geojson-setdata-small

master 24f2135: 13 ms
datastart 1ef4213: 14 ms

geojson-setdata-large

master 24f2135: 125 ms
datastart 1ef4213: 121 ms

@jfirebaugh
Copy link
Contributor

Can you post a full rundown of the new taxonomy -- what are the valid combinations of event types and dataType values, and when are they emitted?

@lucaswoj
Copy link
Contributor Author

Below is the type specification for the data and dataloading events:

interface DataEvent {
    type: 'dataloading' | 'data';
    dataType: (
        'geoJSON' |
        'tileJSON' |
        'style' |
        'sprite' |
        'image' |
        'video' |
        'tile'
    );
}

The Map#data event is fired when any map data loads or changes.

The Map#dataloading event is fired when any map data begins loading or changing asynchronously, to be followed by a data event when the operation is complete.

@jfirebaugh
Copy link
Contributor

But there's also source.load and style.load still?

@jfirebaugh
Copy link
Contributor

What's the rationale for having separate geoJSON, tileJSON, image, and video values for dataType, rather than a single source value? It seems like a source value would simplify the taxonomy and keep it consistent with the domain objects used in rest of the API and in the style specification.

@lucaswoj
Copy link
Contributor Author

lucaswoj commented Sep 27, 2016

But there's also source.load and style.load still?

The plan is to have data subsume those events in a follow-up PR. See the plan in #1715 (comment)

I originally had planned to do so in this PR but the diff size was larger than I liked.

What's the rationale for having separate geoJSON, tileJSON, image, and video values for dataType, rather than a single source value? It seems like a source value would simplify the taxonomy and keep it consistent with the domain objects used in rest of the API and in the style specification.

The rationale was to have a dataType per specific network resource type. Merging geoJSON, tileJSON, image, and video into source is a good idea. I'll push a commit shortly.

@@ -53,6 +53,7 @@ function ImageSource(id, options, dispatcher) {

this.image = image;
this._loaded = true;
this.fire('data', {type: 'image'});
Copy link
Contributor

Choose a reason for hiding this comment

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

typedataType

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@@ -17,6 +17,7 @@ function RasterTileSource(id, options, dispatcher) {
return this.fire('error', err);
}
util.extend(this, tileJSON);
this.fire('data', {type: 'tileJSON'});
Copy link
Contributor

Choose a reason for hiding this comment

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

typedataType

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@@ -7,7 +7,9 @@ module.exports = function(source, numCalls, geojson, cb) {
var startTime = null;
var times = [];

source.on('tile.load', function tileCounter() {
source.on('data', function tileCounter(event) {
if (event.dataType !== 'tile') return;
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this event binding need to distinguish between "tile loading" and "tile unloading"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think so. I will refactor to make this benchmark utility more robust.

function Style(stylesheet, animationLoop, options) {
this.animationLoop = animationLoop || new AnimationLoop();
function Style(stylesheet, map, options) {
this.map = map;
Copy link
Contributor

@jfirebaugh jfirebaugh Sep 27, 2016

Choose a reason for hiding this comment

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

I've been trying to reduce or eliminate the dependency of other class on Map. The dependency here is required by Source#on{Add,Remove}(map) -- we should work towards eliminating those two methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removing the dependency was my first instinct as well. Unfortunately it wasn't straightforward to do so and will require a standalone PR.

Even though the dependency is undesirable, it is a real dependency and should be passed explicitly and simply. As our code is architected, Sources need to know about the state of Camera (which is a mixin on Map). I see #2741 as the proper long-term fix.

@@ -197,23 +197,11 @@ var Map = module.exports = function(options) {
this.style.update(this._classes, {transition: false});
});

this.on('style.change', function() {
this.on('data', function() {
this._update(true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Not every data event should trigger _update(true) -- that triggers updating and recalculating, which is necessary only in certain cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Fortunately it looks like filtering on event.dataType === 'style' is equivalent to listening to the style.change event.

@lucaswoj
Copy link
Contributor Author

All feedback addressed, ready for a second pass of 👀 @jfirebaugh

map.on('source.load', this._update.bind(this));
map.on('source.change', this._update.bind(this));
map.on('source.remove', this._update.bind(this));
map.on('data', this._update.bind(this));
Copy link
Contributor

Choose a reason for hiding this comment

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

Add a dataType === 'source' guard in _update so that it doesn't mutate the DOM more frequently than it did before.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@lucaswoj
Copy link
Contributor Author

Thank you for the review @jfirebaugh 🙇

@lucaswoj lucaswoj merged commit 1d373b0 into master Sep 29, 2016
@lucaswoj lucaswoj deleted the datastart branch September 29, 2016 19:06
captainbarbosa pushed a commit that referenced this pull request Oct 3, 2016
* source.change -> data

* source.load -> sourceload & data

* style.change -> data

* style.load -> data, styleload

* tile.add -> dataloading

* tile.load -> data

* tile.remove -> data

* Unrename "source.load" and "style.load"

* source.add & source.remove -> data

* layer.add & layer.remove -> data

* Remove isDataRemoved flag

* Add documentation for "data" event

* Fix setDataPerf benchmark utility

* Minor doc fix

* Add docs for dataloading event

* merge 'sprite' and 'style' into 'style'

* merge all several types into 'source'

* Refactor "set data perf" benchmark utility

* Ensure "Map#_update(true)" is only called after style mutations

* Ensure Attribution#_update is only called for "data[dataType=source]" events

* Attach Source, not SourceCache, to data events
@strech345
Copy link

is there a 'unload' function?
i need the event which tile is unload.

@lucaswoj
Copy link
Contributor Author

@strech345 Not right now. Defining what it means for a tile to become "unloaded" isn't easy. Is a tile unloaded

  • When it is no longer at the desired zoom level for the current viewport but still rendered?
  • When it is entirely outside of the current viewport?
  • When it is removed from the cache? How big is the cache? Which cache?
  • When it is reloaded?

@strech345
Copy link

@lucaswoj it the same question like for the loading. So i think it should be a equivalent to the dataloading

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

Successfully merging this pull request may close these issues.

3 participants