diff --git a/lib/torque/leaflet/leaflet_tileloader_mixin.js b/lib/torque/leaflet/leaflet_tileloader_mixin.js index 1e72a48..1284099 100644 --- a/lib/torque/leaflet/leaflet_tileloader_mixin.js +++ b/lib/torque/leaflet/leaflet_tileloader_mixin.js @@ -17,6 +17,31 @@ L.Mixin.TileLoader = { this._removeTiles(); }, + visibleTiles: function() { + if (!this._map) { return []; } + var j, i, point, tiles = []; + var bounds = this._map.getPixelBounds(), + zoom = this._map.getZoom(), + tileSize = this.options.tileSize; + + var nwTilePoint = new L.Point( + Math.floor(bounds.min.x / tileSize), + Math.floor(bounds.min.y / tileSize)), + + seTilePoint = new L.Point( + Math.floor(bounds.max.x / tileSize), + Math.floor(bounds.max.y / tileSize)), + + tileBounds = new L.Bounds(nwTilePoint, seTilePoint); + + for (j = tileBounds.min.y; j <= tileBounds.max.y; j++) { + for (i = tileBounds.min.x; i <= tileBounds.max.x; i++) { + tiles.push({ x: i, y: j, z: zoom }); + } + } + return tiles; + }, + _updateTiles: function () { if (!this._map) { return; } diff --git a/lib/torque/leaflet/torque.js b/lib/torque/leaflet/torque.js index 9962b98..f806dcc 100644 --- a/lib/torque/leaflet/torque.js +++ b/lib/torque/leaflet/torque.js @@ -212,24 +212,9 @@ L.TorqueLayer = L.CanvasLayer.extend({ } }, - getHistogramForVisibleRegion: function(varName, callback){ - var tiles= [] - for(var key in this._tiles){ - if (this._tiles[key]) { - tiles.push(this._tiles[key].coord) - } - } - var tilesFilter = this.provider._generateBoundsQuery(tiles); - - this.provider.tablesForFilter(tilesFilter, function(tables) { - // select the table with less than 100k records - var table = tables.filter(function(f) { - return f.nrows < 50000; - }) - table = table.length ? table[0].table_name: this.options.overview_tables[this.options.overview_tables.length - 1] - this.provider.getHistogramForTiles(varName, tiles, table, callback) - }.bind(this)); - + getHistogramForVisibleRegion: function(varName, start, end, bins, callback){ + var tiles = this.visibleTiles(); + this.provider.getHistogramForTiles(varName, start, end, bins, tiles, callback); }, getHistogram:function(variable,callback,noBins){ diff --git a/lib/torque/provider/filterableJson.js b/lib/torque/provider/filterableJson.js index e6ca0dd..dd911a5 100644 --- a/lib/torque/provider/filterableJson.js +++ b/lib/torque/provider/filterableJson.js @@ -439,7 +439,6 @@ var Profiler = require('../profiler'); sql = sql.join(' ') - var query = format(sql,{ x: tile.x, y: tile.y, @@ -453,11 +452,11 @@ var Profiler = require('../profiler'); }) this.sql(query,function(data){ - if(data) { + if (data) { var rows = JSON.parse(data.responseText).rows; callback(rows) } - else{ + else { callback(null) } }) @@ -618,18 +617,48 @@ var Profiler = require('../profiler'); }).join(" or ") }, - getHistogramForTiles: function(varName, tiles, table, callback){ + getHistogramForTiles: function(varName, start, end, bins, tiles, callback) { + var tilesFilter = this._generateBoundsQuery(tiles); + this.tablesForFilter(tilesFilter, function(tables) { + // select the table with less than 100k records + var table = tables.filter(function(f) { + return f.nrows < 50000; + }) + table = table.length ? table[0].table_name: this.options.overview_tables[this.options.overview_tables.length - 1] + this._getHistogramForTiles(varName, start, end, bins, tiles, table, callback) + }.bind(this)); + }, + + _getHistogramForTiles: function(varName, start, end, bins, tiles, table, callback){ + var sql = [ 'with source as (', - 'select {varName} from {table} where ({bounds}) {filters}', - '),', - 'width as (', - 'select min({varName}) as min,', - 'max({varName}) as max,', - '{bins} as buckets', - 'from source', - '),', + 'select {varName} from {table} {tiles} where {bounds} {filters}', + '),' + ] + if (start !== undefined) { + sql = sql.concat([ + 'width as (', + 'select {start} as min,', + '{end} as max,', + '{bins} as buckets', + '),' + ]) + } else { + start = start === undefined ? 'min(' + varName + ')': start; + end = end === undefined ? 'max(' + varName + ')': end; + bins = bins === undefined ? 100: bins; + sql = sql.concat([ + 'width as (', + 'select {start} as min,', + '{end} as max,', + '{bins} as buckets', + 'from source', + '),' + ]) + } + sql = sql.concat([ '_bw as ( select (max - min)/buckets as bw from width ),', 'histogram as (', 'select width_bucket({varName}, min, max, buckets) as bucket,', @@ -640,16 +669,35 @@ var Profiler = require('../profiler'); 'order by bucket', ')', 'select bucket*bw as start, (bucket+1)*bw as end, bucket as bin, lower(range) as min, upper(range) as max, freq from histogram, _bw;' - ] + ]); var filters = this._generateFiltersSQL(false, [varName]) ? " and "+ this._generateFiltersSQL(false, [varName]) : "" + var tiles_query = tiles.map(function (t, i) { + return "xyz2range(" + t.x + "," + t.y + "," + t.z + ") q" + i; + }).join(',') + + if (tiles_query) { + tiles_query = "," + tiles_query + } + + var bounds = tiles.map(function(t, i) { + return format("(quadkey between q{i}.min and q{i}.max)", { i: i }) + }).join('or') + + if (bounds) { + bounds = '(' + bounds + ')'; + } + var query = format(sql.join('\n'), { varName: varName, table: table, + tiles: tiles_query, filters: filters, - bounds: this._generateBoundsQuery(tiles), - bins: 20 + bounds: bounds, + bins: bins, + start: start, + end: end }); var self = this; @@ -663,6 +711,34 @@ var Profiler = require('../profiler'); }); }, + getCatHistogramForTiles: function(varName, tiles, table, callback){ + + var sql = [ + 'select {varName}, count(1) from {table} where ({bounds}) {filters} group by 1 order by 2 desc limit {num_cats}', + ] + + var filters = this._generateFiltersSQL(false, [varName]) ? " and "+ this._generateFiltersSQL(false, [varName]) : "" + + var query = format(sql.join('\n'), { + varName: varName, + table: table, + filters: filters, + bounds: this._generateBoundsQuery(tiles), + num_cats: 20 + }); + + var self = this; + this.sql(query, function (data) { + if (data) { + var rows = JSON.parse(data.responseText).rows; + callback(rows); + } else { + callback(null); + } + }); + }, + + tablesForFilter: function(filter, callback) { var filters = this._generateFiltersSQL() if (filters) {