Compare commits

...

6 Commits

Author SHA1 Message Date
Stuart Lynn
2b61a508b2 adding in the histogram widget and supporting libraries 2015-10-28 18:50:41 -04:00
Stuart Lynn
a27b3aa503 Using one of Mamata's styles 2015-10-28 18:49:21 -04:00
Stuart Lynn
a8c1bf7682 Code to aggregate histograms together 2015-10-28 18:48:45 -04:00
Stuart Lynn
eaba6ee4dc adding filters to be able to filter by multiple conditions 2015-10-28 18:48:22 -04:00
javi
26d0d40e35 histograms tiles 2015-10-28 15:05:34 +01:00
javi
c27b2471da added filters 2015-10-28 14:56:24 +01:00
12 changed files with 60589 additions and 49 deletions

View File

@ -1,16 +1,32 @@
<html>
<link rel="stylesheet" href="vendor/leaflet.css" />
<link rel="stylesheet" href="vendor/cartodb.css" />
<style>
#map, html, body {
width: 100%; height: 100%; padding: 0; margin: 0;
}
#charts{
width: 25%;
min-height: 200px;
position:absolute;
right:20px;
top:10px;
background-color: rgba(255, 255, 255, 0.5);
}
</style>
<body>
<div id="map"></div>
<div id='charts'></div>
<script src="vendor/leaflet.js"></script>
<script src="vendor/leaflet-hash.js"></script>
<script src="../dist/torque.full.uncompressed.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js'></script>
<script src='https://code.jquery.com/jquery-1.11.3.js'></script>
<script src='vendor/underscore.js'></script>
<script src='vendor/backbone.js'></script>
<script src='vendor/histogram-widget.js'></script>
<script>
// define the torque layer style using cartocss
@ -18,49 +34,142 @@
'Map {',
'-torque-time-attribute: "date";',
'-torque-aggregation-function: "count(cartodb_id)";',
'-torque-frame-count: 760;',
'-torque-animation-duration: 15;',
'-torque-resolution: 2',
'-torque-frame-count: 1;',
'-torque-animation-duration: 1;',
'-torque-resolution: 0.25',
// '@8: #15f4ee;',
// '@10: white',
'}',
'#layer {',
' marker-width: 3;',
' marker-fill-opacity: 0.8;',
' marker-fill: #FEE391; ',
' comp-op: "lighten";',
' [value > 2] { marker-fill: #FEC44F; }',
' [value > 3] { marker-fill: #FE9929; }',
' [value > 4] { marker-fill: #EC7014; }',
' [value > 5] { marker-fill: #CC4C02; }',
' [value > 6] { marker-fill: #993404; }',
' [value > 7] { marker-fill: #662506; }',
' [frame-offset = 1] { marker-width: 10; marker-fill-opacity: 0.05;}',
' [frame-offset = 2] { marker-width: 15; marker-fill-opacity: 0.02;}',
'}'
].join('\n');
'marker-opacity: 0.2;',
'image-filters: colorize-alpha(#078c88,#15f4ee,#b7fcfa,#f8fffe);',
'marker-line-width: 0;',
'marker-fill: #2E5387;',
'marker-allow-overlap: true;',
'marker-width: 1;',
//'[zoom=4]{marker-width: 0.25;}',
//'[zoom=5]{marker-width: 0.75;}',
//'[zoom=6]{marker-width: 1.25;}',
//'[zoom=7]{marker-width: 1.5;}',
//'[zoom=8]{marker-width: 1.75;}',
//'[zoom=9]{marker-width: 2.0;}',
//'[zoom=10]{marker-width: 2.25;}',
// '[zoom=11]{marker-width: 2.5;marker-opacity:0.3;}',
// '[zoom=12]{marker-width: 2.75;marker-opacity:0.4;}',
// '[zoom=13]{marker-width: 3.0;marker-opacity:0.45;}',
// '[zoom=14]{marker-width: 3.25;marker-opacity:0.5;}',
// '[zoom=15]{marker-width: 0.75;marker-opacity:0.55;}',
// '[zoom=16]{marker-width: 4.0;marker-opacity:0.6;}',
// '[zoom>=17]{marker-width: 4.25;marker-opacity:0.65;}',
/*
' [value > 2] { marker-fill: #FEC44F; }',
' [value > 10] { marker-fill: #FE9929; }',
' [value > 20] { marker-fill: #EC7014; }',
' [value > 30] { marker-fill: #CC4C02; }',
' [value > 40] { marker-fill: #993404; }',
' [value > 50] { marker-fill: #662506; }',
*/
]
// for(var i =0; i <255; i += 10){
// var color = d3.scale.linear()
// .range()
// .range(["steelblue", "brown"])
// .interpolate(d3.interpolateHcl);
// CARTOCSS.push("[value >= "+i+"]{marker-fill:"+color(i)+"};")
// }
CARTOCSS.push("}")
CARTOCSS= CARTOCSS.join(" ")
console.log(CARTOCSS)
/*
['Map {,',
'-torque-time-attribute: "date";,',
'-torque-aggregation-function: "count(cartodb_id)";,',
'-torque-frame-count: 6;,',
'-torque-animation-duration: 15;,',
'-torque-resolution: 1,',
'},',
'#layer {,',
'marker-width: 1;,',
'marker-fill-opacity: 1.0;,',
'marker-fill: #FF2900; ,',
'[ value <= 20] { marker-fill: #0C2C84; } ',
'[ value <= 8] { marker-fill: #225EA8; }',
'[ value <= 4] { marker-fill: #1D91C0; }',
'[ value <= 3] { marker-fill: #41B6C4; }',
'[ value <= 2] { marker-fill: #7FCDBB; }',
'[ value <= 1] { marker-fill: #C7E9B4; }',
'[ value <= 0.1] { marker-fill: #FFFFCC; }',
'}'].join('\n')
*/
var map = new L.Map('map', {
zoomControl: true,
center: [40, 0],
zoom: 3
zoom: 0
});
var hash = new L.Hash(map);
L.tileLayer('http://{s}.api.cartocdn.com/base-dark/{z}/{x}/{y}.png', {
L.tileLayer('http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', {
attribution: 'CartoDB'
}).addTo(map);
var torqueLayer = new L.TorqueLayer({
tileJSON: "http://localhost:4000/examples/tilejson.json",
cartocss: CARTOCSS
tileJSON: "http://bigdata.int.cartodb.net:8888/tile.json",
//tileJSON: "http://10.0.180.5:1234/",
cartocss: CARTOCSS,
valueDataType: Float32Array
});
torqueLayer.error(function(err){
for(error in err){
console.warn(err[error]);
console.warn([error]);
}
});
torqueLayer.addTo(map);
torqueLayer.play()
torqueLayer.setStep(0);
//torqueLayer.play()
window.torqueLayer = torqueLayer;
function createHistogram(variable){
var div_id = "histogram_"+variable;
$("#charts").append("<svg id='"+div_id+"'><g class='Canvas'></g></svg>")
var chart = new cdb.geo.ui.Widget.Histogram.Chart(
{el:$("#"+div_id),
data:{values: [1,3,4,5,22,1,3], labels:[22, 34, 45, 67,100, 200, 300]},
width: 300,
height:100,
y:0,
x:0})
chart.render()
chart.show()
chart.bind("fliterChanged",function(start,end){
console.log('setting filter', start,end )
torqueLayer.setQueryFilter(variable, [start,end] )
})
torqueLayer.addEventListener("histogramAdded",function(data){
var data = torqueLayer.histogramForVariable(variable)
chart.reset(data)
})
}
createHistogram(0)
</script>
</body>
</html>

1894
examples/vendor/backbone.js vendored Normal file

File diff suppressed because it is too large Load Diff

2
examples/vendor/cartodb.css vendored Normal file

File diff suppressed because one or more lines are too long

55281
examples/vendor/cartodb.uncompressed.js vendored Normal file

File diff suppressed because one or more lines are too long

1277
examples/vendor/histogram-widget.js vendored Normal file

File diff suppressed because it is too large Load Diff

146
examples/vendor/leaflet-hash.js vendored Normal file
View File

@ -0,0 +1,146 @@
(function(window) {
var HAS_HASHCHANGE = (function() {
var doc_mode = window.documentMode;
return ('onhashchange' in window) &&
(doc_mode === undefined || doc_mode > 7);
})();
L.Hash = function(map) {
this.onHashChange = L.Util.bind(this.onHashChange, this);
if (map) {
this.init(map);
}
};
L.Hash.prototype = {
map: null,
lastHash: null,
parseHash: function(hash) {
if(hash.indexOf('#') == 0) {
hash = hash.substr(1);
}
var args = hash.split("/");
if (args.length == 3) {
var zoom = parseInt(args[0], 10),
lat = parseFloat(args[1]),
lon = parseFloat(args[2]);
if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) {
return false;
} else {
return {
center: new L.LatLng(lat, lon),
zoom: zoom
};
}
} else {
return false;
}
},
formatHash: function(map) {
var center = map.getCenter(),
zoom = map.getZoom(),
precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
return "#" + [zoom,
center.lat.toFixed(precision),
center.lng.toFixed(precision)
].join("/");
},
init: function(map) {
this.map = map;
this.map.on("moveend", this.onMapMove, this);
// reset the hash
this.lastHash = null;
this.onHashChange();
if (!this.isListening) {
this.startListening();
}
},
remove: function() {
this.map = null;
if (this.isListening) {
this.stopListening();
}
},
onMapMove: function(map) {
// bail if we're moving the map (updating from a hash),
// or if the map has no zoom set
if (this.movingMap || this.map.getZoom() === 0) {
return false;
}
var hash = this.formatHash(this.map);
if (this.lastHash != hash) {
location.replace(hash);
this.lastHash = hash;
}
},
movingMap: false,
update: function() {
var hash = location.hash;
if (hash === this.lastHash) {
// console.info("(no change)");
return;
}
var parsed = this.parseHash(hash);
if (parsed) {
// console.log("parsed:", parsed.zoom, parsed.center.toString());
this.movingMap = true;
this.map.setView(parsed.center, parsed.zoom);
this.movingMap = false;
} else {
// console.warn("parse error; resetting:", this.map.getCenter(), this.map.getZoom());
this.onMapMove(this.map);
}
},
// defer hash change updates every 100ms
changeDefer: 100,
changeTimeout: null,
onHashChange: function() {
// throttle calls to update() so that they only happen every
// `changeDefer` ms
if (!this.changeTimeout) {
var that = this;
this.changeTimeout = setTimeout(function() {
that.update();
that.changeTimeout = null;
}, this.changeDefer);
}
},
isListening: false,
hashChangeInterval: null,
startListening: function() {
if (HAS_HASHCHANGE) {
L.DomEvent.addListener(window, "hashchange", this.onHashChange);
} else {
clearInterval(this.hashChangeInterval);
this.hashChangeInterval = setInterval(this.onHashChange, 50);
}
this.isListening = true;
},
stopListening: function() {
if (HAS_HASHCHANGE) {
L.DomEvent.removeListener(window, "hashchange", this.onHashChange);
} else {
clearInterval(this.hashChangeInterval);
}
this.isListening = false;
}
};
})(window);

14
examples/vendor/torque.category.css vendored Normal file
View File

@ -0,0 +1,14 @@
.torque-category .axis text {
font: 10px sans-serif;
fill: white;
}
.torque-category .bars rect {
fill: steelblue;
}
.torque-category .axis path,
.torque-category .axis line {
fill: none;
stroke: #FFF;
shape-rendering: crispEdges;
}

140
examples/vendor/torque.category.js vendored Normal file
View File

@ -0,0 +1,140 @@
// histogram widget for torque
// d3 based
function categoryChart() {
var margin = {top: 0, right: 0, bottom: 20, left: 0},
width = 960,
height = 500;
var histogram = d3.layout.histogram(),
x = d3.scale.ordinal(),
y = d3.scale.linear(),
xAxis = d3.svg.axis().scale(x).orient("bottom").tickSize(6, 0);
function chart(selection) {
var colors = ['#0000b4','#0082ca','#0094ff','#0d4bcf','#0066AE','#074285','#00187B','#285964','#405F83','#416545','#4D7069','#6E9985','#7EBC89','#0283AF','#79BCBF','#99C19E'];
selection.each(function(data) {
// Compute the histogram.
// data = histogram(data);
// Update the x-scale.
x .domain(data.map(function(d) { return d[0]; }))
.rangeRoundBands([0, width - margin.left - margin.right], .1);
// Update the y-scale.
y .domain([])
.range([height - margin.top - margin.bottom, 0]);
colorScale = d3.scale.quantize()
.domain([0,data.map(function(d){return d[0]}).length])
.range(colors);
// Select the svg element, if it exists.
var svg = d3.select(this).selectAll("svg")
.attr('class', 'torque-histogram')
.data([data]);
// Otherwise, create the skeletal chart.
var gEnter = svg.enter().append("svg").append("g");
gEnter.append("g").attr("class", "bars");
gEnter.append("g").attr("class", "x axis");
// Update the outer dimensions.
svg .attr("width", width)
.attr("height", height);
// Update the inner dimensions.
var g = svg.select("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Update the bars.
var bar = svg.select(".bars").selectAll(".bar").data(data);
bar.enter().append("rect").attr('class', 'bar');
bar.exit().remove();
bar .attr("width", x.rangeBand())
.attr("x", function(d) { return x(d[0]); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y.range()[0] - y(d[1]); })
.order();
// Update the x-axis.
g.select(".x.axis")
.attr("transform", "translate(0," + y.range()[0] + ")")
.call(xAxis);
});
}
chart.margin = function(_) {
if (!arguments.length) return margin;
margin = _;
return chart;
};
chart.width = function(_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function(_) {
if (!arguments.length) return height;
height = _;
return chart;
};
// Expose the histogram's value, range and bins method.
d3.rebind(chart, histogram, "value", "range", "bins");
// Expose the x-axis' tickFormat method.
d3.rebind(chart, xAxis, "tickFormat");
return chart;
}
torque.widgets = torque.widgets || {}
/**
* creates an histogram inside the element based on torque data
* @id any d3 valid selector
* @torqueLayer torque layer objecy
* @returns histogram widget object
*/
torque.widgets.category = function(id, torqueLayer, variable) {
function updateVategories() {
var values = torqueLayer.totalHistogramFor(variable, 0);
var el = d3.select(id)
.datum(values)
var size = el.node().getBoundingClientRect()
el.call(histogramChart()
.width(size.width)
.height(size.height)
.bins(10)
.tickFormat(d3.format(".0f"))
);
};
// public API
var h = {
disable: function() {
// torqueLayer.off('change:time', updateHistogram);
// torqueLayer.off('tilesLoaded', updateHistogram);
// torqueLayer.off('tileLoaded', updateHistogram);
torqueLayer.off('histLoaded', updateHistogram);
},
enable: function() {
// torqueLayer.on('change:time', updateHistogram);
// torqueLayer.on('tilesLoaded', updateHistogram);
// torqueLayer.on('tileLoaded', updateHistogram);
torqueLayer.on('histLoaded', updateHistogram);
}
}
h.enable();
return h;
}

1548
examples/vendor/underscore.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,8 @@ L.TorqueLayer = L.CanvasLayer.extend({
if (!torque.isBrowserSupported()) {
throw new Error("browser is not supported by torque");
}
this._histograms = []
options.tileLoader = true;
this.key = 0;
this.prevRenderedKey = 0;
@ -93,6 +95,10 @@ L.TorqueLayer = L.CanvasLayer.extend({
// don't load tiles that are not being shown
if (t.zoom !== self._map.getZoom()) return;
self._tileLoaded(t, tileData);
if(Object.keys(tileData.histogram).length > 0){
self._addTileHistogram(t.x,t.y,t.zoom, tileData.histogram)
}
self._clearTileCaches();
if (tileData) {
self.redraw();
@ -102,7 +108,66 @@ L.TorqueLayer = L.CanvasLayer.extend({
}, this);
},
_invalidateHistograms:function(){
this._histograms= undefined
},
_addTileHistogram:function(x, y, zoom, histogram){
this._histograms[zoom] = this._histograms[zoom] ? this._histograms[zoom] : []
this._histograms[zoom][x] = this._histograms[zoom][x] ? this._histograms[zoom][x] : []
this._histograms[zoom][x][y] = histogram
this.fire('histogramAdded')
},
histogramForVariable:function(variable){
histogramParts=[]
Object.keys(this._tiles).forEach(function(t){
var coord = t.split(":")
if(this._histograms[coord[2]][coord[0]][coord[1]]){
histogramParts.push(this._histograms[coord[2]][coord[0]][coord[1]][variable])
}
}.bind(this))
return this._combineHistograms(histogramParts)
},
_transfromHistogram:function(hist,zoom){
var new_hist= {
bins: {},
bounds: hist.bounds,
zoom : zoom,
x: 0
}
var zoom_diff = zoom - hist.zoom;
new_hist.x = hist.x >> zoom_diff;
for( var k in hist.bins){
var idx = (hist.x + k ) >> (zoom_diff - new_hist.x)
new_hist.bins[idx] = new_hist.bins[idx] || 0
new_hist.bins[idx] += hist.bins[k]
}
return new_hist
},
_combineHistograms:function(histograms){
var minZoom = Math.min.apply(null,histograms.map(function(h){return h.zoom}))
var mappedHistograms = histograms.map(function(h){return this._transfromHistogram(h,minZoom)}.bind(this))
var combinedHistogram = {
bins:{},
bounds: [
Math.min.apply(null,mappedHistograms.map(function(h){return h.bounds[0]})),
Math.max.apply(null,mappedHistograms.map(function(h){return h.bounds[1]}))
],
zoom: minZoom,
x: 0
}
mappedHistograms.forEach(function(histogram){
Object.keys(histogram.bins).forEach(function(bin){
combinedHistogram.bins[bin] = combinedHistogram.bins[bin] || 0
combinedHistogram.bins[bin]+= histogram.bins[bin]
})
})
return combinedHistogram
},
_clearTileCaches: function() {
var t, tile;
for(t in this._tiles) {
@ -332,6 +397,11 @@ L.TorqueLayer = L.CanvasLayer.extend({
return this.provider.getKeySpan();
},
setQueryFilter:function(variable, start,end){
this.provider.setFilter(variable,start,end)
},
/**
* set the cartocss for the current renderer
*/

View File

@ -19,6 +19,7 @@
var tileJSON = function (options) {
this._ready = false;
this._tileQueue = [];
this._filters = {}
this.options = options;
this.options.coordinates_data_type = this.options.coordinates_data_type || Uint8Array;
@ -39,6 +40,23 @@
NAME: "tileJSON",
filterRange: function(attrIndex, min, max) {
this.filter = attrIndex + "=" + min + "," + max;
this.reload()
},
filterExact: function(attrIndex, exact) {
this.filter = attrIndex + "=" + exact
this.reload()
},
_setFilter:function(variable, start,end){
this._filters[variable] = {type:'range', start:start,end:end)
this.reload();
},
_removefilter:function(variable){
this._filters[variable] = undefined
},
/**
* return the torque tile encoded in an efficient javascript
* structure:
@ -48,10 +66,43 @@
* Index: Array index to the properties
* }
*/
proccessTile: function(rows, coord, zoom) {
createProccessTileWorker:function(){
var workerFunction = "var proccessTile ="+ this.proccessTileSerial.toString()
var wrapper = "; self.onmessage = function(e){var data = JSON.parse(e.data); JSON.stringify(self.postMessage(proccessTile(data.response,data.coord, data.zoom, data.options)))}"
var script = workerFunction + wrapper;
var blob = new Blob([script], {type: "text/javascript"})
var worker = new Worker(window.URL.createObjectURL(blob))
return worker
},
proccessTile:function(response,coord,zoom,callback){
if(typeof(Worker) === "undefined"){
callback(this.proccessTileSerial(response,coord,zoom, this.options))
}
else{
var worker = this.createProccessTileWorker()
worker.onmessage = function(e){
callback(e.data)
worker.terminate()
}
var workerSafeOptions= {
cumulative: this.options.cumulative,
valueDataType: this.options.valueDataType,
resolution: this.options.resolution,
}
worker.postMessage(JSON.stringify({response: response, coord: {x:coord.x,y:coord.y}, zoom:zoom, options: workerSafeOptions}))
}
},
proccessTileSerial: function(response, coord, zoom,options) {
var r;
var x = new this.options.coordinates_data_type(rows.length);
var y = new this.options.coordinates_data_type(rows.length);
var data = JSON.parse(response)
var rows = data.pixels
var histograms = data.histograms
var x = options.x || new Uint8Array(rows.length);
var y = options.y || new Uint8Array(rows.length);
// count number of dates
var dates = 0;
@ -64,16 +115,16 @@
}
}
if(this.options.cumulative) {
if(options.cumulative) {
dates = (1 + maxDateSlots) * rows.length;
}
var type = this.options.cumulative ? Uint32Array: Uint8ClampedArray;
var type = options.cumulative ? Uint32Array: Uint8ClampedArray;
// reserve memory for all the dates
var timeIndex = new Int32Array(maxDateSlots + 1); //index-size
var timeCount = new Int32Array(maxDateSlots + 1);
var renderData = new (this.options.valueDataType || type)(dates);
var renderData = new (options.valueDataType || type)(dates);
var renderDataPos = new Uint32Array(dates);
var rowsPerSlot = {};
@ -81,15 +132,15 @@
// precache pixel positions
for (var r = 0; r < rows.length; ++r) {
var row = rows[r];
x[r] = row.x__uint8 * this.options.resolution;
y[r] = row.y__uint8 * this.options.resolution;
x[r] = row.x__uint8 * options.resolution;
y[r] = row.y__uint8 * options.resolution;
var dates = row.dates__uint16;
var vals = row.vals__uint8;
if (!this.options.cumulative) {
if (!options.cumulative) {
for (var j = 0, len = dates.length; j < len; ++j) {
var rr = rowsPerSlot[dates[j]] || (rowsPerSlot[dates[j]] = []);
if(this.options.cumulative) {
if(options.cumulative) {
vals[j] += prev_val;
}
prev_val = vals[j];
@ -148,11 +199,12 @@
timeIndex: timeIndex,
renderDataPos: renderDataPos,
renderData: renderData,
maxDate: maxDateSlots
maxDate: maxDateSlots,
histogram: histograms
};
},
setSteps: function(steps, opt) {
setSteps: function(steps, opt) {
opt = opt || {};
if (this.options.steps !== steps) {
this.options.steps = steps;
@ -249,15 +301,18 @@
var limit_x = Math.pow(2, zoom);
var corrected_x = ((coord.x % limit_x) + limit_x) % limit_x;
var index = Math.abs(corrected_x + coord.y) % subdomains.length;
var url = this.templateUrl
var url = this.tileUrls[(corrected_x + coord.y) % this.tileUrls.length]
.replace('{x}', corrected_x)
.replace('{y}', coord.y)
.replace('{z}', zoom)
.replace('{s}', subdomains[index])
if (this.filter) {
url += "?" + this.filter
}
torque.net.get( url , function (data) {
if (data && data.responseText) {
var rows = JSON.parse(data.responseText);
callback(self.proccessTile(rows, coord, zoom));
self.proccessTile(data.responseText, coord, zoom,callback.bind(self));
} else {
callback(null);
}
@ -314,6 +369,7 @@
_fetchMap: function(callback) {
var self = this;
torque.net.get(this.options.tileJSON, function (data) {
data = JSON.parse(data.response);
if (data) {
@ -325,6 +381,7 @@
self.options[k] = data[k];
}
self.templateUrl = data.tiles[0];
self.tileUrls = data.tiles;
if (self.templateUrl.indexOf("http") !== 0){
self.templateUrl = self.options.tileJSON.substring(0, self.options.tileJSON.lastIndexOf("/") + 1) + self.templateUrl;
}
@ -334,4 +391,4 @@
}
};
module.exports = tileJSON;
module.exports = tileJSON;

View File

@ -44,14 +44,16 @@ function getTile(jsonRelPath, cartocss, z, x, y, step, callback) {
var canvas = new Canvas(256, 256);
var pointRenderer = new torque.renderer.Point(canvas, rendererOptions);
provider.proccessTile(rows, {x: x, y: y}, z, function(tile){
pointRenderer.renderTile(tile, step, function(err) {
if (err) {
return callback(err, null);
}
pointRenderer.applyFilters();
return callback(null, canvas);
});
}.bind(this));
pointRenderer.renderTile(provider.proccessTile(rows, {x: x, y: y}, z), step, function(err) {
if (err) {
return callback(err, null);
}
pointRenderer.applyFilters();
return callback(null, canvas);
});
}
module.exports = {