Adding the ability for leafletLayer to filter data on the client side and to calculate histograms and value arrays for the variables stored in the currently loaded tiles.
This commit is contained in:
parent
130d72c872
commit
fd5bc0f732
@ -10,6 +10,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
||||
|
||||
providers: {
|
||||
'sql_api': torque.providers.json,
|
||||
'filterable_sql_api': torque.providers.filterableJson,
|
||||
'url_template': torque.providers.JsonArray,
|
||||
'windshaft': torque.providers.windshaft,
|
||||
'tileJSON': torque.providers.tileJSON
|
||||
@ -28,6 +29,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
||||
options.tileLoader = true;
|
||||
this.key = 0;
|
||||
this.prevRenderedKey = 0;
|
||||
this._filters = {}
|
||||
if (options.cartocss) {
|
||||
torque.extend(options, torque.common.TorqueLayer.optionsFromCartoCSS(options.cartocss));
|
||||
}
|
||||
@ -98,11 +100,42 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
||||
self.redraw();
|
||||
}
|
||||
self.fire('tileLoaded');
|
||||
self.fire('dataUpdate')
|
||||
});
|
||||
}, this);
|
||||
|
||||
},
|
||||
|
||||
setFilters:function(){
|
||||
this.provider.setFilters(this._filters);
|
||||
this._reloadTiles();
|
||||
return this;
|
||||
},
|
||||
filterByRange:function(variableName,start,end){
|
||||
this._filters[variableName] = {type: 'range', range: {start:start,end:end}}
|
||||
this._filtersChanged()
|
||||
this.fire('dataUpdate')
|
||||
return this
|
||||
},
|
||||
filterByCat:function(variableName,categories){
|
||||
this._filters[variableName] = {type: 'cat', categories: categories}
|
||||
this._filtersChanged()
|
||||
return this
|
||||
},
|
||||
clearFilter: function(name){
|
||||
if(name){
|
||||
delete this._filters[name]
|
||||
}
|
||||
else{
|
||||
this._filters = {}
|
||||
}
|
||||
this._filtersChanged()
|
||||
return this
|
||||
},
|
||||
_filtersChanged:function(){
|
||||
this._clearTileCaches()
|
||||
this._render()
|
||||
},
|
||||
_clearTileCaches: function() {
|
||||
var t, tile;
|
||||
for(t in this._tiles) {
|
||||
@ -112,12 +145,101 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_clearCaches: function() {
|
||||
this.renderer && this.renderer.clearSpriteCache();
|
||||
this._clearTileCaches();
|
||||
},
|
||||
|
||||
valuesForRangeVariable:function(variable){
|
||||
var t, tile;
|
||||
|
||||
var variable_id = this.provider.idForRange(variable)
|
||||
var values = [ ]
|
||||
|
||||
|
||||
for(t in this._tiles){
|
||||
tile = this._tiles[t]
|
||||
var noPoints = tile.x.length;
|
||||
for(var i=0; i < tile.x.length; i++){
|
||||
if(tile.renderFlags[i] ){
|
||||
value = tile.renderData[variable_id*noPoints + i]
|
||||
values.push(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return values;
|
||||
},
|
||||
|
||||
valuesForCatVariable:function(variable){
|
||||
var t, tile;
|
||||
|
||||
var categories = this.provider.idsForCategory(variable)
|
||||
var result = [ ]
|
||||
|
||||
|
||||
for(t in this._tiles){
|
||||
tile = this._tiles[t]
|
||||
var noPoints = tile.x.length;
|
||||
for(var i=0; i < tile.x.length; i++){
|
||||
if(tile.renderFlags[i] ){
|
||||
var vals={}
|
||||
Object.keys(categories).forEach(function(categoryName){
|
||||
var variable_id = categories[categoryName]
|
||||
value = tile.renderData[variable_id*noPoints + i]
|
||||
vals[categoryName] = value
|
||||
}.bind(this))
|
||||
result.push(vals)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
},
|
||||
getValues:function(variable,callback){
|
||||
var type= this.provider._mapping[variable].type
|
||||
if(type=='float'){
|
||||
callback(this.valuesForRangeVariable(variable))
|
||||
}
|
||||
else{
|
||||
callback(this.valuesForCatVariable(variable))
|
||||
}
|
||||
},
|
||||
getHistogram:function(variable,callback,noBins){
|
||||
var type= this.provider._mapping[variable].type
|
||||
if(type=='float'){
|
||||
callback(this.histogramForRangeVariable(variable,noBins))
|
||||
}
|
||||
else if(type='cat'){
|
||||
callback(this.histogramForCatVariable(variable))
|
||||
}
|
||||
return this
|
||||
},
|
||||
histogramForCatVariable:function(variable){
|
||||
var result = {}
|
||||
this.valuesForCatVariable(variable).forEach(function(point){
|
||||
Object.keys(point).forEach(function(key){
|
||||
result[key] = result[key] || 0
|
||||
result[key] += point[key]
|
||||
})
|
||||
})
|
||||
return result
|
||||
},
|
||||
histogramForRangeVariable:function(variable,noBins){
|
||||
noBins = noBins || 10
|
||||
|
||||
var vals = this.valuesForRangeVariable(variable)
|
||||
|
||||
var min = Math.min.apply(null, vals)
|
||||
var max = Math.max.apply(null, vals)
|
||||
var binSize = (max-min)/noBins
|
||||
var result = []
|
||||
vals.forEach(function(val){
|
||||
var bin = (val -min)/binSize
|
||||
result[bin]= result[bin] || 0
|
||||
result[bin] += val
|
||||
})
|
||||
return result
|
||||
},
|
||||
onAdd: function (map) {
|
||||
map.on({
|
||||
'zoomend': this._clearCaches,
|
||||
@ -208,6 +330,38 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
||||
canvas.width = canvas.width;
|
||||
},
|
||||
|
||||
_filterTile:function(tile){
|
||||
var noPoints = tile.x.length
|
||||
|
||||
var renderFlags = []
|
||||
for(var i =0; i < noPoints; i++){
|
||||
var includePoint = true
|
||||
Object.keys(this._filters).forEach(function(key){
|
||||
var filter = this._filters[key]
|
||||
var variableId = this.provider.idForRange(key)
|
||||
|
||||
var value = tile.renderData[variableId*noPoints+i]
|
||||
if(filter.type=='range'){
|
||||
if(value < filter.range.start || value > filter.range.end){
|
||||
includePoint = false;
|
||||
}
|
||||
}
|
||||
else if (filter.type=='cat'){
|
||||
var ids = this.provider.idsForCategory(key);
|
||||
filter.categories.forEach(function(key){
|
||||
var catId = ids[key]
|
||||
var value = tile.renderData[catId*noPoints + i]
|
||||
if(value==0){
|
||||
includePoint= false
|
||||
}
|
||||
}.bind(this))
|
||||
}
|
||||
}.bind(this))
|
||||
renderFlags[i] = includePoint
|
||||
}
|
||||
return renderFlags
|
||||
},
|
||||
|
||||
/**
|
||||
* render the selectef key
|
||||
* don't call this function directly, it's called by
|
||||
@ -236,10 +390,13 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
||||
// all the points
|
||||
this.renderer._ctx.drawImage(tile._tileCache, 0, 0);
|
||||
} else {
|
||||
|
||||
tile.renderFlags=this._filterTile(tile)
|
||||
this.renderer.renderTile(tile, this.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.renderer.applyFilters();
|
||||
|
||||
// prepare caches if the animation is not running
|
||||
|
@ -57,7 +57,7 @@ var Filters = require('./torque_filters');
|
||||
this.TILE_SIZE = 256;
|
||||
this._style = null;
|
||||
this._gradients = {};
|
||||
|
||||
|
||||
this._forcePoints = false;
|
||||
}
|
||||
|
||||
@ -165,14 +165,14 @@ var Filters = require('./torque_filters');
|
||||
i.src = canvas.toDataURL();
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
return canvas;
|
||||
},
|
||||
|
||||
//
|
||||
// renders all the layers (and frames for each layer) from cartocss
|
||||
//
|
||||
renderTile: function(tile, key, callback) {
|
||||
renderTile: function(tile, key, renderFlags, callback) {
|
||||
if (this._iconsToLoad > 0) {
|
||||
this.on('allIconsLoaded', function() {
|
||||
this.renderTile.apply(this, [tile, key, callback]);
|
||||
@ -193,7 +193,7 @@ var Filters = require('./torque_filters');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
prof.end(true);
|
||||
|
||||
return callback && callback(null);
|
||||
@ -237,12 +237,11 @@ var Filters = require('./torque_filters');
|
||||
},
|
||||
|
||||
//
|
||||
// renders a tile in the canvas for key defined in
|
||||
// renders a tile in the canvas for key defined in
|
||||
// the torque tile
|
||||
//
|
||||
_renderTile: function(tile, key, frame_offset, sprites, shader, shaderVars) {
|
||||
if (!this._canvas) return;
|
||||
|
||||
var prof = Profiler.metric('torque.renderer.point.renderTile').start();
|
||||
var ctx = this._ctx;
|
||||
var blendMode = compop2canvas(shader.eval('comp-op')) || this.options.blendmode;
|
||||
@ -254,27 +253,29 @@ var Filters = require('./torque_filters');
|
||||
key = tile.maxDate;
|
||||
}
|
||||
var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1)
|
||||
var activePixels = tile.timeCount[key];
|
||||
var activePixels = tile.x.length;
|
||||
var anchor = this.options.resolution/2;
|
||||
if (activePixels) {
|
||||
var pixelIndex = tile.timeIndex[key];
|
||||
var pixelIndex = 0;//tile.timeIndex[key];
|
||||
for(var p = 0; p < activePixels; ++p) {
|
||||
var posIdx = tile.renderDataPos[pixelIndex + p];
|
||||
var c = tile.renderData[pixelIndex + p];
|
||||
if (c) {
|
||||
var sp = sprites[c];
|
||||
if (sp === undefined) {
|
||||
sp = sprites[c] = this.generateSprite(shader, c, torque.extend({ zoom: tile.z, 'frame-offset': frame_offset }, shaderVars));
|
||||
}
|
||||
if (sp) {
|
||||
var x = tile.x[posIdx]- (sp.width >> 1) + anchor;
|
||||
var y = tileMax - tile.y[posIdx] + anchor; // flip mercator
|
||||
ctx.drawImage(sp, x, y - (sp.height >> 1));
|
||||
}
|
||||
if(tile.renderFlags[p]){
|
||||
var posIdx = tile.renderDataPos[pixelIndex + p];
|
||||
var c = tile.renderData[pixelIndex + p];
|
||||
if (c) {
|
||||
var sp = sprites[c];
|
||||
if (sp === undefined) {
|
||||
sp = sprites[c] = this.generateSprite(shader, c, torque.extend({ zoom: tile.z, 'frame-offset': frame_offset }, shaderVars));
|
||||
}
|
||||
if (sp) {
|
||||
var x = tile.x[posIdx]- (sp.width >> 1) + anchor;
|
||||
var y = tileMax - tile.y[posIdx] + anchor; // flip mercator
|
||||
ctx.drawImage(sp, x, y - (sp.height >> 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
prof.end(true);
|
||||
},
|
||||
@ -442,7 +443,7 @@ var Filters = require('./torque_filters');
|
||||
}
|
||||
gradient = {};
|
||||
var colorize = this._style['image-filters'].args;
|
||||
|
||||
|
||||
var increment = 1/colorize.length;
|
||||
for (var i = 0; i < colorize.length; i++){
|
||||
var key = increment * i + increment;
|
||||
|
Loading…
Reference in New Issue
Block a user