Skip to content

Commit

Permalink
add `map.getSourceTileData(sourceID, params, cb)
Browse files Browse the repository at this point in the history
`getSourceTileData` calls the callback with an array of GeoJSON
FeatureCollections (one for each tile).

fixes #2106
  • Loading branch information
ansis committed Feb 13, 2016
1 parent 08bc5b0 commit f5b0a00
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 1 deletion.
1 change: 1 addition & 0 deletions js/source/geojson_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ GeoJSONSource.prototype = util.inherit(Evented, /** @lends GeoJSONSource.prototy

featuresAt: Source._vectorFeaturesAt,
featuresIn: Source._vectorFeaturesIn,
getTileData: Source._getVectorTileData,

_updateData: function() {
this._dirty = false;
Expand Down
8 changes: 8 additions & 0 deletions js/source/image_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ ImageSource.prototype = util.inherit(Evented, {
return callback(null, []);
},

/**
* An ImageSource doesn't have any tiled data.
* @private
*/
getTileData: function(params, callback) {
return callback(null, []);
},

serialize: function() {
return {
type: 'image',
Expand Down
4 changes: 4 additions & 0 deletions js/source/raster_tile_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,9 @@ RasterTileSource.prototype = util.inherit(Evented, {

featuresIn: function(bbox, params, callback) {
callback(null, []);
},

getTileData: function(params, callback) {
return callback(null, []);
}
});
31 changes: 31 additions & 0 deletions js/source/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,37 @@ exports._vectorFeaturesIn = function(bounds, params, classes, zoom, bearing, cal
});
};

exports._getVectorTileData = function(params, callback) {
if (!this._pyramid) {
return callback(null, []);
}

var pyramid = this._pyramid;
var tiles = pyramid.renderedIDs().map(function(id) {
return pyramid.getTile(id);
});

var dataTiles = {};
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
var dataID = new TileCoord(Math.min(tile.sourceMaxZoom, tile.coord.z), tile.coord.x, tile.coord.y, 0).id;
if (!dataTiles[dataID]) {
dataTiles[dataID] = tile;
}
}

util.asyncAll(Object.keys(dataTiles), function(dataID, callback) {
var tile = dataTiles[dataID];
this.dispatcher.send('get tile data', {
uid: tile.uid,
source: this.id,
params: params
}, callback, tile.workerID);
}.bind(this), function(err, results) {
callback(err, results.filter(function(x) { return !!x; }));
});
};

/*
* Create a tiled data source instance given an options object
*
Expand Down
1 change: 1 addition & 0 deletions js/source/vector_tile_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ VectorTileSource.prototype = util.inherit(Evented, {

featuresAt: Source._vectorFeaturesAt,
featuresIn: Source._vectorFeaturesIn,
getTileData: Source._getVectorTileData,

_loadTile: function(tile) {
var overscaling = tile.coord.z > this.maxzoom ? Math.pow(2, tile.coord.z - this.maxzoom) : 1;
Expand Down
4 changes: 4 additions & 0 deletions js/source/video_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ VideoSource.prototype = util.inherit(Evented, /** @lends VideoSource.prototype *
return callback(null, []);
},

getTileData: function(params, callback) {
return callback(null, []);
},

serialize: function() {
return {
type: 'video',
Expand Down
9 changes: 9 additions & 0 deletions js/source/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,14 @@ util.extend(Worker.prototype, {
} else {
callback(null, []);
}
},

'get tile data': function(params, callback) {
var tile = this.loaded[params.source] && this.loaded[params.source][params.uid];
if (tile) {
callback(null, tile.getData(params.params));
} else {
callback(null, null);
}
}
});
28 changes: 28 additions & 0 deletions js/source/worker_tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
var FeatureTree = require('../data/feature_tree');
var CollisionTile = require('../symbol/collision_tile');
var Bucket = require('../data/bucket');
var featureFilter = require('feature-filter');

module.exports = WorkerTile;

Expand All @@ -21,6 +22,7 @@ function WorkerTile(params) {
WorkerTile.prototype.parse = function(data, layers, actor, callback) {

this.status = 'parsing';
this.data = data;

var collisionTile = new CollisionTile(this.angle, this.pitch);
this.featureTree = new FeatureTree(this.coord, this.overscaling, collisionTile);
Expand Down Expand Up @@ -242,3 +244,29 @@ function getTransferables(buffers) {
}
return transferables;
}

WorkerTile.prototype.getData = function(params) {
if (!this.data) return null;

var layer = this.data.layers ?
this.data.layers[params['source-layer']] :
this.data;

if (!layer) return null;

var filter = featureFilter(params.filter);

var features = [];
for (var i = 0; i < layer.length; i++) {
var feature = layer.feature(i);
if (filter(feature)) {
features.push(feature.toGeoJSON(this.coord.x, this.coord.y, this.coord.z));
}
}

return {
type: 'FeatureCollection',
features: features,
coord: { z: this.coord.z, x: this.coord.x, y: this.coord.y }
};
};
25 changes: 24 additions & 1 deletion js/ui/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ var Map = module.exports = function(options) {
this.jumpTo(options);
}

this.sources = {};
this.stacks = {};
this._classes = {};

Expand Down Expand Up @@ -373,6 +372,30 @@ util.extend(Map.prototype, /** @lends Map.prototype */{
return this;
},


/**
* Get data from vector tiles as an array of GeoJSON FeatureCollections FeatureCollections.
*
* @param {string} sourceID source ID
* @param {Object} params
* @param {string} [params.source-layer] The name of the vector tile layer to get features from.
* @param {Array} [params.filter] A mapbox-gl-style-spec filter.
* @param {callback} callback function that receives the results
*
* @returns {Map} `this`
*/
getSourceTileData: function(sourceID, params, callback) {
var source = this.getSource(sourceID);

if (!source) {
return callback("No source with id '" + sourceID + "'.", []);
}

source.getTileData(params, callback);

return this;
},

/**
* Apply multiple style mutations in a batch
*
Expand Down
52 changes: 52 additions & 0 deletions test/js/source/worker_tile.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ var test = require('prova');
var WorkerTile = require('../../../js/source/worker_tile');
var Wrapper = require('../../../js/source/geojson_wrapper');
var TileCoord = require('../../../js/source/tile_coord');
var vt = require('vector-tile');
var fs = require('fs');
var path = require('path');
var Protobuf = require('pbf');

test('basic', function(t) {
var buckets = [{
Expand Down Expand Up @@ -47,3 +51,51 @@ test('basic', function(t) {
});
});
});

test('getData', function(t) {
var features = [{
type: 1,
geometry: [0, 0],
tags: { oneway: true }
}];


t.test('geojson tile', function(t) {
var tile = new WorkerTile({uid: '', zoom: 0, maxZoom: 20, tileSize: 512, source: 'source',
coord: new TileCoord(1, 1, 1), overscaling: 1 });

t.equal(tile.getData({}), null);

tile.data = new Wrapper(features);

t.equal(tile.getData({}).type, 'FeatureCollection');
t.equal(tile.getData({}).features.length, 1);
t.deepEqual(tile.getData({}).features[0].properties, features[0].tags);
t.equal(tile.getData({ filter: ['==', 'oneway', true]}).features.length, 1);
t.equal(tile.getData({ filter: ['!=', 'oneway', true]}).features.length, 0);
t.end();
});

t.test('vector tile', function(t) {
var tile = new WorkerTile({uid: '', zoom: 0, maxZoom: 20, tileSize: 512, source: 'source',
coord: new TileCoord(1, 1, 1), overscaling: 1 });

t.equal(tile.getData({}), null);

tile.data = new vt.VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(path.join(__dirname, '/../../fixtures/mbsv5-6-18-23.vector.pbf')))));

t.equal(tile.getData({ 'source-layer': 'does-not-exist'}), null);

var roads = tile.getData({ 'source-layer': 'road' });
t.equal(roads.type, 'FeatureCollection');
t.equal(roads.features.length, 3);

t.equal(tile.getData({ 'source-layer': 'road', filter: ['==', 'class', 'main'] }).features.length, 1);
t.equal(tile.getData({ 'source-layer': 'road', filter: ['!=', 'class', 'main'] }).features.length, 2);

t.end();
});


t.end();
});

0 comments on commit f5b0a00

Please sign in to comment.