diff --git a/src/source/geojson_source.js b/src/source/geojson_source.js index 6eaa669e9a8..2bc3a41082e 100644 --- a/src/source/geojson_source.js +++ b/src/source/geojson_source.js @@ -180,7 +180,13 @@ class GeoJSONSource extends Evented { showCollisionBoxes: this.map.showCollisionBoxes }; - tile.workerID = this.dispatcher.send('loadTile', params, (err, data) => { + if (!tile.workerID || tile.state === 'expired') { + tile.workerID = this.dispatcher.send('loadTile', params, done.bind(this), this.workerID); + } else { + tile.workerID = this.dispatcher.send('reloadTile', params, done.bind(this), this.workerID); + } + + function done(err, data) { tile.unloadVectorData(); @@ -199,8 +205,7 @@ class GeoJSONSource extends Evented { } return callback(null); - - }, this.workerID); + } } abortTile(tile) { diff --git a/src/source/geojson_worker_source.js b/src/source/geojson_worker_source.js index 3a76200c6a4..3a54de36701 100644 --- a/src/source/geojson_worker_source.js +++ b/src/source/geojson_worker_source.js @@ -83,6 +83,7 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource { this._indexData(data, params, (err, indexed) => { if (err) { return callback(err); } this._geoJSONIndexes[params.source] = indexed; + this.loaded[params.source] = {}; callback(null); }); }.bind(this); @@ -90,6 +91,27 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource { this.loadGeoJSON(params, handleData); } + /** + * Implements {@link WorkerSource#reloadTile}. + * + * If the tile is loaded, uses the implementation in VectorTileWorkerSource. + * Otherwise, such as after a setData() call, we load the tile fresh. + * + * @param {Object} params + * @param {string} params.source The id of the source for which we're loading this tile. + * @param {string} params.uid The UID for this tile. + */ + reloadTile(params, callback) { + const loaded = this.loaded[params.source], + uid = params.uid; + + if (loaded && loaded[uid]) { + return super.reloadTile(params, callback); + } else { + return this.loadTile(params, callback); + } + } + /** * Fetch and parse GeoJSON according to the given params. Calls `callback` * with `(err, data)`, where `data` is a parsed GeoJSON object. diff --git a/test/unit/source/geojson_worker_source.test.js b/test/unit/source/geojson_worker_source.test.js index 7b9e17ef224..e5ac51d62cb 100644 --- a/test/unit/source/geojson_worker_source.test.js +++ b/test/unit/source/geojson_worker_source.test.js @@ -3,6 +3,7 @@ const test = require('mapbox-gl-js-test').test; const GeoJSONWorkerSource = require('../../../src/source/geojson_worker_source'); const StyleLayerIndex = require('../../../src/style/style_layer_index'); +const TileCoord = require('../../../src/source/tile_coord'); test('removeSource', (t) => { t.test('removes the source from _geoJSONIndexes', (t) => { @@ -48,3 +49,82 @@ test('removeSource', (t) => { t.end(); }); + +test('reloadTile', (t) => { + t.test('does not rebuild vector data unless data has changed', (t) => { + const layers = [ + { + id: 'mylayer', + source: 'sourceId', + type: 'symbol', + } + ]; + const layerIndex = new StyleLayerIndex(layers); + const source = new GeoJSONWorkerSource(null, layerIndex); + const originalLoadVectorData = source.loadVectorData; + let loadVectorCallCount = 0; + source.loadVectorData = function(params, callback) { + loadVectorCallCount++; + return originalLoadVectorData.call(this, params, callback); + }; + const geoJson = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [0, 0] + } + }; + const tileParams = { + source: 'sourceId', + uid: 0, + coord: new TileCoord(0, 0, 0), + maxZoom: 10 + }; + + function addData(callback) { + source.loadData({ source: 'sourceId', data: JSON.stringify(geoJson) }, (err) => { + t.equal(err, null); + callback(); + }); + } + + function reloadTile(callback) { + source.reloadTile(tileParams, (err, data) => { + t.equal(err, null); + return callback(data); + }); + } + + addData(() => { + // first call should load vector data from geojson + let firstData; + reloadTile(data => { + firstData = data; + }); + t.equal(loadVectorCallCount, 1); + + // second call won't give us new rawTileData + reloadTile(data => { + t.notOk('rawTileData' in data); + data.rawTileData = firstData.rawTileData; + t.deepEqual(data, firstData); + }); + + // also shouldn't call loadVectorData again + t.equal(loadVectorCallCount, 1); + + // replace geojson data + addData(() => { + // should call loadVectorData again after changing geojson data + reloadTile(data => { + t.ok('rawTileData' in data); + t.deepEqual(data, firstData); + }); + t.equal(loadVectorCallCount, 2); + t.end(); + }); + }); + }); + + t.end(); +});