Skip to content

Commit 38b4581

Browse files
author
Anand Thakker
committed
Merge pull request mapbox#2982 from mapbox/fix-add-source-type
Revise addSourceType to be independent of Map instance
2 parents 527213c + 92dac06 commit 38b4581

File tree

7 files changed

+215
-95
lines changed

7 files changed

+215
-95
lines changed

js/mapbox-gl.js

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mapboxgl.Popup = require('./ui/popup');
1414
mapboxgl.Marker = require('./ui/marker');
1515

1616
mapboxgl.Style = require('./style/style');
17+
mapboxgl.Source = require('./source/source');
1718

1819
mapboxgl.LngLat = require('./geo/lng_lat');
1920
mapboxgl.LngLatBounds = require('./geo/lng_lat_bounds');

js/source/source.js

+40-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict';
22

3+
var Evented = require('../util/evented');
34
var util = require('../util/util');
45

56
var sourceTypes = {
@@ -10,6 +11,10 @@ var sourceTypes = {
1011
'image': require('../source/image_source')
1112
};
1213

14+
var coreTypes = ['vector', 'raster', 'geojson', 'video', 'image'];
15+
16+
var Source = module.exports = util.extend({}, Evented);
17+
1318
/*
1419
* Creates a tiled data source instance given an options object.
1520
*
@@ -19,7 +24,7 @@ var sourceTypes = {
1924
* @param {Dispatcher} dispatcher
2025
* @returns {Source}
2126
*/
22-
exports.create = function(id, source, dispatcher) {
27+
Source.create = function(id, source, dispatcher) {
2328
source = new sourceTypes[source.type](id, source, dispatcher);
2429

2530
if (source.id !== id) {
@@ -30,14 +35,44 @@ exports.create = function(id, source, dispatcher) {
3035
return source;
3136
};
3237

33-
exports.getType = function (name) {
38+
Source.getType = function (name) {
3439
return sourceTypes[name];
3540
};
3641

37-
exports.setType = function (name, type) {
42+
Source.setType = function (name, type) {
3843
sourceTypes[name] = type;
3944
};
4045

46+
/**
47+
* Returns the names of any registered non-core source types.
48+
* @private
49+
*/
50+
Source.getCustomTypeNames = function () {
51+
return Object.keys(sourceTypes).filter(function (type) {
52+
return coreTypes.indexOf(type) < 0;
53+
});
54+
};
55+
56+
/**
57+
* Adds a [custom source type](#Custom Sources), making it available for use with
58+
* {@link Map#addSource}.
59+
* @private
60+
* @param {string} name The name of the source type; source definition objects use this name in the `{type: ...}` field.
61+
* @param {Function} SourceType A {@link Source} constructor.
62+
*/
63+
Source.addType = function (name, SourceType) {
64+
if (Source.getType(name)) {
65+
throw new Error('A source type named ' + name + ' already exists.');
66+
}
67+
68+
Source.setType(name, SourceType);
69+
70+
// an internal event, used to notify Style instances that there is a new
71+
// custom source type.
72+
Source.fire('_add', { name: name });
73+
};
74+
75+
4176
/**
4277
* The `Source` interface must be implemented by each source type, including "core" types (`vector`, `raster`, `video`, etc.) and all custom, third-party types.
4378
*
@@ -46,7 +81,7 @@ exports.setType = function (name, type) {
4681
*
4782
* @param {string} id The id for the source. Must not be used by any existing source.
4883
* @param {Object} options Source options, specific to the source type (except for `options.type`, which is always required).
49-
* @param {string} options.type The source type, matching the value of `name` used in {@link Style#addSourceType}.
84+
* @param {string} options.type The source type, matching the value of `name` used in {@link Source.addType}.
5085
* @param {Dispatcher} dispatcher A {@link Dispatcher} instance, which can be used to send messages to the workers.
5186
*
5287
* @fires load to indicate source data has been loaded, so that it's okay to call `loadTile`
@@ -116,7 +151,7 @@ exports.setType = function (name, type) {
116151
* implementation may also be targeted by the {@link Source} via
117152
* `dispatcher.send('source-type.methodname', params, callback)`.
118153
*
119-
* @see {@link Map#addSourceType}
154+
* @see {@link Source.addType}
120155
* @private
121156
*
122157
* @class WorkerSource

js/style/style.js

+43-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict';
22

3+
var assert = require('assert');
34
var Evented = require('../util/evented');
45
var StyleLayer = require('./style_layer');
56
var ImageSprite = require('./image_sprite');
@@ -37,11 +38,15 @@ function Style(stylesheet, animationLoop, workerCount) {
3738
'_forwardSourceEvent',
3839
'_forwardTileEvent',
3940
'_forwardLayerEvent',
40-
'_redoPlacement'
41+
'_redoPlacement',
42+
'_handleAddSourceType',
43+
'_registerCustomSource'
4144
], this);
4245

4346
this._resetUpdates();
4447

48+
Source.on('_add', this._handleAddSourceType);
49+
4550
var stylesheetLoaded = function(err, stylesheet) {
4651
if (err) {
4752
this.fire('error', {error: err});
@@ -70,11 +75,19 @@ function Style(stylesheet, animationLoop, workerCount) {
7075
this.fire('load');
7176
}.bind(this);
7277

73-
if (typeof stylesheet === 'string') {
74-
ajax.getJSON(normalizeURL(stylesheet), stylesheetLoaded);
75-
} else {
76-
browser.frame(stylesheetLoaded.bind(this, null, stylesheet));
77-
}
78+
// register any existing custom source types with the workers
79+
util.asyncAll(Source.getCustomTypeNames(), this._registerCustomSource, function (err) {
80+
if (err) {
81+
this.fire('error', {error: err});
82+
return;
83+
}
84+
85+
if (typeof stylesheet === 'string') {
86+
ajax.getJSON(normalizeURL(stylesheet), stylesheetLoaded);
87+
} else {
88+
browser.frame(stylesheetLoaded.bind(this, null, stylesheet));
89+
}
90+
}.bind(this));
7891

7992
this.on('source.load', function(event) {
8093
var source = event.source;
@@ -669,23 +682,6 @@ Style.prototype = util.inherit(Evented, {
669682
return source ? QueryFeatures.source(source, params) : [];
670683
},
671684

672-
addSourceType: function (name, SourceType, callback) {
673-
if (Source.getType(name)) {
674-
return callback(new Error('A source type called "' + name + '" already exists.'));
675-
}
676-
677-
Source.setType(name, SourceType);
678-
679-
if (!SourceType.workerSourceURL) {
680-
return callback(null, null);
681-
}
682-
683-
this.dispatcher.broadcast('load worker source', {
684-
name: name,
685-
url: SourceType.workerSourceURL
686-
}, callback);
687-
},
688-
689685
_handleErrors: function(validate, key, value, throws, props) {
690686
var action = throws ? validateStyle.throwErrors : validateStyle.emitErrors;
691687
var result = validate.call(validateStyle, util.extend({
@@ -697,8 +693,32 @@ Style.prototype = util.inherit(Evented, {
697693
return action.call(validateStyle, this, result);
698694
},
699695

696+
_handleAddSourceType: function (event) {
697+
this._registerCustomSource(event.name, function (err) {
698+
if (err) {
699+
this.fire('error', {error: err});
700+
return;
701+
}
702+
this.fire('source-type.add', event);
703+
}.bind(this));
704+
},
705+
706+
_registerCustomSource: function (name, callback) {
707+
var SourceType = Source.getType(name);
708+
assert(SourceType);
709+
if (SourceType.workerSourceURL) {
710+
this.dispatcher.broadcast('load worker source', {
711+
name: name,
712+
url: SourceType.workerSourceURL
713+
}, callback);
714+
} else {
715+
callback();
716+
}
717+
},
718+
700719
_remove: function() {
701720
this.dispatcher.remove();
721+
Source.off('_add', this._handleAddSourceType);
702722
},
703723

704724
_reloadSource: function(id) {

js/ui/map.js

+7-13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ var window = require('../util/browser').window;
77
var Evented = require('../util/evented');
88
var DOM = require('../util/dom');
99

10+
var Source = require('../source/source');
1011
var Style = require('../style/style');
1112
var AnimationLoop = require('../style/animation_loop');
1213
var Painter = require('../render/painter');
@@ -202,6 +203,11 @@ var Map = module.exports = function(options) {
202203
this.resize();
203204

204205
if (options.classes) this.setClasses(options.classes);
206+
if (options.sourceTypes) {
207+
for (var sourceTypeName in options.sourceTypes) {
208+
Source.addType(sourceTypeName, options.sourceTypes[sourceTypeName]);
209+
}
210+
}
205211
if (options.style) this.setStyle(options.style);
206212
if (options.attributionControl) this.addControl(new Attribution(options.attributionControl));
207213

@@ -685,7 +691,7 @@ util.extend(Map.prototype, /** @lends Map.prototype */{
685691
* @param {string} id The ID of the source to add. Must not conflict with existing sources.
686692
* @param {Object} source The source object, conforming to the
687693
* Mapbox Style Specification's [source definition](https://www.mapbox.com/mapbox-gl-style-spec/#sources).
688-
* @param {string} source.type The source type, which must be either one of the core Mapbox GL source types defined in the style specification or a custom type that has been added to the map with {@link Map#addSourceType}.
694+
* @param {string} source.type The source type, which must be one of the core Mapbox GL source types defined in the style specification.
689695
* @fires source.add
690696
* @returns {Map} `this`
691697
*/
@@ -695,18 +701,6 @@ util.extend(Map.prototype, /** @lends Map.prototype */{
695701
return this;
696702
},
697703

698-
/**
699-
* Adds a [custom source type](#Custom Sources), making it available for use with
700-
* {@link Map#addSource}.
701-
* @private
702-
* @param {string} name The name of the source type; source definition objects use this name in the `{type: ...}` field.
703-
* @param {Function} SourceType A {@link Source} constructor.
704-
* @param {Function} callback Called when the source type is ready or with an error argument if there is an error.
705-
*/
706-
addSourceType: function (name, SourceType, callback) {
707-
return this.style.addSourceType(name, SourceType, callback);
708-
},
709-
710704
/**
711705
* Removes a source from the map's style.
712706
*

test/js/source/source.test.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
3+
var test = require('tap').test;
4+
var Source = require('../../../js/source/source');
5+
6+
test('Source', function (t) {
7+
t.test('#getCustomTypeNames', function (t) {
8+
t.same(Source.getCustomTypeNames(), []);
9+
Source.addType('source.test.type-1', function () {});
10+
t.same(Source.getCustomTypeNames(), ['source.test.type-1']);
11+
t.end();
12+
});
13+
14+
t.test('#addType', function (t) {
15+
function SourceType () {}
16+
Source.on('_add', onAdd);
17+
18+
t.plan(2);
19+
Source.addType('source.test.type-2', SourceType);
20+
t.equal(Source.getType('source.test.type-2'), SourceType);
21+
function onAdd (event) {
22+
t.equal(event.name, 'source.test.type-2');
23+
Source.off('_add', onAdd);
24+
}
25+
});
26+
27+
t.test('#addType throws for duplicate source type', function (t) {
28+
Source.addType('source.test.type-3', function () {});
29+
t.throws(function () {
30+
Source.addType('source.test.type-3', function () {});
31+
});
32+
t.end();
33+
});
34+
35+
t.end();
36+
});

0 commit comments

Comments
 (0)