Skip to content

Commit

Permalink
Proof of concept - vector source w/ dynamic property data
Browse files Browse the repository at this point in the history
  • Loading branch information
Anand Thakker committed Jul 14, 2016
1 parent bd64c2a commit ae67686
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 42 deletions.
71 changes: 29 additions & 42 deletions debug/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,51 +65,38 @@
map.addControl(new mapboxgl.Geolocate());

map.on('load', function() {
map.addSource('geojson', {
"type": "geojson",
"data": "data/linestring.geojson"
});

map.addLayer({
"id": "route",
"type": "line",
"source": "geojson",
"paint": {
"line-color": "#EC8D8D",
"line-width": {
"base": 1.5,
"stops": [
[
5,
0.75
],
[
18,
32
map.addSourceType('vector-dynamic', DynamicVectorSource, function () {
map.addSource('iowa', {
"type": "vector-dynamic",
"url": "mapbox://devseed.ia-pop-z10"
});

map.addLayer({
"id": "dots",
"type": "circle",
"source": "iowa",
"source-layer": "ia",
"paint": {
"circle-color": '#000000',
"circle-opacity": 0.2,
"circle-radius": {
"property": "foo",
"base": 1,
"stops": [
[
0,
10
],
[
100,
1
]
]
]
}
}
}
}, 'country-label-lg');

var bufferTimes = {};
map.on('tile.stats', function(bufferTimes) {
var _stats = [];
for (var name in bufferTimes) {
var value = Math.round(bufferTimes[name]);
if (isNaN(value)) continue;
var width = value;
_stats.push({name: name, value: value, width: width});
}
_stats = _stats.sort(function(a, b) { return b.value - a.value }).slice(0, 10);

var html = '';
for (var i in _stats) {
html += '<div style="width:' + _stats[i].width * 2 + 'px">' + _stats[i].value + 'ms - ' + _stats[i].name + '</div>';
}
}, 'country-label-lg');

document.getElementById('buffer').innerHTML = html;
});
})
});

map.on('click', function(e) {
Expand Down
160 changes: 160 additions & 0 deletions js/source/dynamic_vector_source.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
'use strict';

var webworkify = require('webworkify');
var Bucket = require('../data/bucket');
var VectorTileSource = require('./vector_tile_source');
var vt = require('vector-tile');
var Protobuf = require('pbf');
var normalizeURL = require('../util/mapbox').normalizeTileURL;
var util = require('../util/util');

module.exports = DynamicVectorSource;

function DynamicVectorSource(id, options, dispatcher) {
VectorTileSource.call(this, id, options, dispatcher);
this.tileFeatures = {};
}

DynamicVectorSource.workerSourceURL = URL.createObjectURL(
webworkify(require('./dynamic_vector_worker'), { bare: true })
);

DynamicVectorSource.prototype = util.inherit(VectorTileSource, {
type: 'vector-dynamic',
update: function(data) {
this._dirty = true;
console.info('Updating with random data; TODO: accept new data here and join against user-configured id property');
this.fire('change');
},
loadTile: function(tile, callback) {
if (this._dirty && tile.loaded) {
return this.updateTile(tile, callback);
}

var overscaling = tile.coord.z > this.maxzoom ? Math.pow(2, tile.coord.z - this.maxzoom) : 1;
var params = {
type: this.type,
url: normalizeURL(tile.coord.url(this.tiles, this.maxzoom, this.scheme), this.url),
uid: tile.uid,
coord: tile.coord,
zoom: tile.coord.z,
tileSize: this.tileSize * overscaling,
source: this.id,
overscaling: overscaling,
angle: this.map.transform.angle,
pitch: this.map.transform.pitch,
showCollisionBoxes: this.map.showCollisionBoxes
};

if (tile.workerID) {
params.rawTileData = tile.rawTileData;
this.dispatcher.send('reload tile', params, done.bind(this), tile.workerID);
} else {
tile.workerID = this.dispatcher.send('load tile', params, done.bind(this));
}

function done(err, data) {
if (tile.aborted)
return;

if (err) {
return callback(err);
}

tile.loadVectorData(data, this.map.style);

// save serialized arrays even after creating the buckets
// so we can transfer them back during updateTile
for (var b = 0; b < data.buckets.length; b++) {
var bucket = data.buckets[b];
tile.buckets[bucket.layerId].arrayGroups = bucket.arrays;
}

var layers = this.tileFeatures[tile.uid] = {};
this.vtLayers = new vt.VectorTile(new Protobuf(new Uint8Array(tile.rawTileData))).layers;
for (var id in this.vtLayers) {
var features = layers[id] = [];
var layer = this.vtLayers[id];
for (var i = 0; i < layer.length; i++) {
features.push(layer.feature(i));
}
}

if (tile.redoWhenDone) {
tile.redoWhenDone = false;
tile.redoPlacement(this);
}

callback(null);
}
},

getTilePropertyData: function (tile) {
return this.tileFeatures[tile.uid];
},

updateTile: function(tile, callback) {
var prevData = this.getTilePropertyData(tile);

// dummy data; TODO: make it real
var newData = {};
for (var layer in prevData) {
newData[layer] = [];
for (var i = 0; i < prevData[layer].length; i++) {
newData[layer].push({ foo: Math.random() * 100 });
}
}

var params = {
uid: tile.uid,
source: this.id,
data: newData,
arrayGroups: util.mapObject(tile.buckets, function (b) { return b.arrayGroups; })
};
var transferables = [];
if (tile.workerID) {
var buckets = tile.buckets;
for (var id in buckets) {
if (!buckets[id].updateFeatureProperties) continue;
buckets[id].getTransferables(transferables);
}
this.dispatcher.send(this.type + '.updateTile', params, done.bind(this), tile.workerID, transferables);
} else {
// load + update
}

function done (err, data) {
tile.buckets = unserializeBuckets(data.buckets, this.map.style);

// save serialized arrays even after creating the buckets
// so we can transfer them back during updateTile
for (var b = 0; b < data.buckets.length; b++) {
var bucket = data.buckets[b];
tile.buckets[bucket.layerId].arrayGroups = bucket.arrays;
}

callback(err);
}
}
});

function unserializeBuckets(input, style) {
// Guard against the case where the map's style has been set to null while
// this bucket has been parsing.
if (!style) return;

var output = {};
for (var i = 0; i < input.length; i++) {
var layer = style.getLayer(input[i].layerId);
if (!layer) continue;

var bucket = Bucket.create(util.extend({
layer: layer,
childLayers: input[i].childLayerIds
.map(style.getLayer.bind(style))
.filter(function(layer) { return layer; })
}, input[i]));
output[bucket.id] = bucket;
}
return output;
}
25 changes: 25 additions & 0 deletions js/source/dynamic_vector_worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';
var VectorTileWorkerSource = require('./vector_tile_worker_source');
var util = require('../util/util');

module.exports = function (self) {
self.registerWorkerSource('vector-dynamic', DynamicVectorWorker);
};

function DynamicVectorWorker (actor, styleLayers, loadVectorData) {
VectorTileWorkerSource.call(this, actor, styleLayers, loadVectorData);
}

DynamicVectorWorker.prototype = util.inherit(VectorTileWorkerSource, {
updateTile: function (params, callback) {
var source = params.source;
var uid = params.uid;
if (!this.loaded[source][uid]) {
return callback();
}

var layerFamilies = this.styleLayers.getLayerFamilies();
var tile = this.loaded[source][uid];
tile.updateProperties(params.data, layerFamilies, this.actor, params.arrayGroups, callback);
}
});
3 changes: 3 additions & 0 deletions js/source/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

var util = require('../util/util');

// TEMPORARY
window.DynamicVectorSource = require('../source/dynamic_vector_source');

var sourceTypes = {
'vector': require('../source/vector_tile_source'),
'raster': require('../source/raster_tile_source'),
Expand Down

0 comments on commit ae67686

Please sign in to comment.