Merge branch 'master' of github.com:CartoDB/torque into data-peek
This commit is contained in:
commit
a2696195f4
@ -1,3 +1,6 @@
|
|||||||
|
before_install:
|
||||||
|
- sudo apt-get install -y pkg-config libcairo2-dev libjpeg8-dev libgif-dev
|
||||||
|
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- "0.10"
|
- "0.10"
|
||||||
|
17
NEWS
17
NEWS
@ -1,3 +1,20 @@
|
|||||||
|
2.11.1
|
||||||
|
|
||||||
|
2.11.0
|
||||||
|
- Do not fix values in the edge (#147)
|
||||||
|
- Windshaft provider accepts an optional data type for coordinates (#149)
|
||||||
|
- Acceptance tests
|
||||||
|
- renderTile accepts a callback to be called when rendering finishes
|
||||||
|
- tile rendering deferred until all assets are loaded
|
||||||
|
- sprite rendering now scales source to marker dimensions
|
||||||
|
- Filters accept a canvas class
|
||||||
|
|
||||||
|
2.10.1
|
||||||
|
- Adjusted point position taking resolution into account
|
||||||
|
- Changed loop: false to pause animation at last frame
|
||||||
|
- On torque-frame-count: 1, always pause for better performance
|
||||||
|
- Fixes getTimeSpan
|
||||||
|
|
||||||
2.10.0
|
2.10.0
|
||||||
- Adds timetostep method to torque layer
|
- Adds timetostep method to torque layer
|
||||||
- Function qualifyURL can be provided to not rely on document's one
|
- Function qualifyURL can be provided to not rely on document's one
|
||||||
|
14
dist/torque.full.js
vendored
14
dist/torque.full.js
vendored
File diff suppressed because one or more lines are too long
529
dist/torque.full.uncompressed.js
vendored
529
dist/torque.full.uncompressed.js
vendored
File diff suppressed because it is too large
Load Diff
6
dist/torque.js
vendored
6
dist/torque.js
vendored
File diff suppressed because one or more lines are too long
406
dist/torque.uncompressed.js
vendored
406
dist/torque.uncompressed.js
vendored
@ -48,6 +48,9 @@ var cancelAnimationFrame = global.cancelAnimationFrame
|
|||||||
this.running = true;
|
this.running = true;
|
||||||
requestAnimationFrame(this._tick);
|
requestAnimationFrame(this._tick);
|
||||||
this.options.onStart && this.options.onStart();
|
this.options.onStart && this.options.onStart();
|
||||||
|
if(this.options.steps === 1){
|
||||||
|
this.running = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
isRunning: function() {
|
isRunning: function() {
|
||||||
@ -82,6 +85,7 @@ var cancelAnimationFrame = global.cancelAnimationFrame
|
|||||||
this.range = torque.math.linear(0, this.options.steps);
|
this.range = torque.math.linear(0, this.options.steps);
|
||||||
this.rangeInv = this.range.invert();
|
this.rangeInv = this.range.invert();
|
||||||
this.time(this._time);
|
this.time(this._time);
|
||||||
|
this.start();
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -120,9 +124,12 @@ var cancelAnimationFrame = global.cancelAnimationFrame
|
|||||||
this._t0 = t1;
|
this._t0 = t1;
|
||||||
this._time += delta;
|
this._time += delta;
|
||||||
if(this.step() >= this.options.steps) {
|
if(this.step() >= this.options.steps) {
|
||||||
this._time = 0;
|
|
||||||
if(!this.options.loop){
|
if(!this.options.loop){
|
||||||
this.stop();
|
// set time to max time
|
||||||
|
this.time(this.options.animationDuration);
|
||||||
|
this.pause();
|
||||||
|
} else {
|
||||||
|
this._time = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(this.running) {
|
if(this.running) {
|
||||||
@ -663,6 +670,7 @@ module.exports.TorqueLayer = TorqueLayer;
|
|||||||
var types = {
|
var types = {
|
||||||
Uint8Array: typeof(global['Uint8Array']) !== 'undefined' ? global.Uint8Array : Array,
|
Uint8Array: typeof(global['Uint8Array']) !== 'undefined' ? global.Uint8Array : Array,
|
||||||
Uint32Array: typeof(global['Uint32Array']) !== 'undefined' ? global.Uint32Array : Array,
|
Uint32Array: typeof(global['Uint32Array']) !== 'undefined' ? global.Uint32Array : Array,
|
||||||
|
Int16Array: typeof(global['Int16Array']) !== 'undefined' ? global.Int16Array : Array,
|
||||||
Int32Array: typeof(global['Int32Array']) !== 'undefined' ? global.Int32Array: Array
|
Int32Array: typeof(global['Int32Array']) !== 'undefined' ? global.Int32Array: Array
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1577,7 +1585,7 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
|
|
||||||
providers: {
|
providers: {
|
||||||
'sql_api': torque.providers.json,
|
'sql_api': torque.providers.json,
|
||||||
'url_template': torque.providers.jsonarray,
|
'url_template': torque.providers.JsonArray,
|
||||||
'windshaft': torque.providers.windshaft
|
'windshaft': torque.providers.windshaft
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1703,7 +1711,7 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
if (tile) {
|
if (tile) {
|
||||||
pos = this.getTilePos(tile.coord);
|
pos = this.getTilePos(tile.coord);
|
||||||
ctx.setTransform(1, 0, 0, 1, pos.x, pos.y);
|
ctx.setTransform(1, 0, 0, 1, pos.x, pos.y);
|
||||||
this.renderer.renderTile(tile, this.key, pos.x, pos.y);
|
this.renderer.renderTile(tile, this.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1754,6 +1762,14 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
return new Date(time);
|
return new Date(time);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
timeToStep: function(timestamp) {
|
||||||
|
if (typeof timestamp === "Date") timestamp = timestamp.getTime();
|
||||||
|
if (!this.provider) return 0;
|
||||||
|
var times = this.provider.getKeySpan();
|
||||||
|
var step = (this.provider.getSteps() * (timestamp - times.start)) / (times.end - times.start);
|
||||||
|
return step;
|
||||||
|
},
|
||||||
|
|
||||||
getStep: function() {
|
getStep: function() {
|
||||||
return this.key;
|
return this.key;
|
||||||
},
|
},
|
||||||
@ -1908,7 +1924,7 @@ module.exports.GMapsTileLoader = gmaps.GMapsTileLoader;
|
|||||||
module.exports.GMapsTorqueLayer = gmaps.GMapsTorqueLayer;
|
module.exports.GMapsTorqueLayer = gmaps.GMapsTorqueLayer;
|
||||||
module.exports.GMapsTiledTorqueLayer = gmaps.GMapsTiledTorqueLayer;
|
module.exports.GMapsTiledTorqueLayer = gmaps.GMapsTiledTorqueLayer;
|
||||||
|
|
||||||
},{"./animator":1,"./cartocss_reference":2,"./common":3,"./core":4,"./gmaps":8,"./leaflet":12,"./math":15,"./mercator":16,"./provider":18,"./renderer":23,"./request":26}],11:[function(require,module,exports){
|
},{"./animator":1,"./cartocss_reference":2,"./common":3,"./core":4,"./gmaps":8,"./leaflet":12,"./math":15,"./mercator":16,"./provider":18,"./renderer":23,"./request":27}],11:[function(require,module,exports){
|
||||||
require('./leaflet_tileloader_mixin');
|
require('./leaflet_tileloader_mixin');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2323,7 +2339,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
|
|
||||||
providers: {
|
providers: {
|
||||||
'sql_api': torque.providers.json,
|
'sql_api': torque.providers.json,
|
||||||
'url_template': torque.providers.jsonarray,
|
'url_template': torque.providers.JsonArray,
|
||||||
'windshaft': torque.providers.windshaft
|
'windshaft': torque.providers.windshaft
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2394,14 +2410,11 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
this.provider = new this.providers[this.options.provider](options);
|
this.provider = new this.providers[this.options.provider](options);
|
||||||
this.renderer = new this.renderers[this.options.renderer](this.getCanvas(), options);
|
this.renderer = new this.renderers[this.options.renderer](this.getCanvas(), options);
|
||||||
|
|
||||||
|
this.renderer.on("allIconsLoaded", this.render.bind(this));
|
||||||
|
|
||||||
|
|
||||||
// for each tile shown on the map request the data
|
// for each tile shown on the map request the data
|
||||||
this.on('tileAdded', function(t) {
|
this.on('tileAdded', function(t) {
|
||||||
var fixedPoint = new L.Point(t.x, t.y);
|
|
||||||
this._adjustTilePoint(fixedPoint);
|
|
||||||
t.corrected = {};
|
|
||||||
t.corrected.x = fixedPoint.x;
|
|
||||||
t.corrected.y = fixedPoint.y;
|
|
||||||
var tileData = this.provider.getTileData(t, t.zoom, function(tileData) {
|
var tileData = this.provider.getTileData(t, t.zoom, function(tileData) {
|
||||||
// don't load tiles that are not being shown
|
// don't load tiles that are not being shown
|
||||||
if (t.zoom !== self._map.getZoom()) return;
|
if (t.zoom !== self._map.getZoom()) return;
|
||||||
@ -2415,39 +2428,6 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_adjustTilePoint: function (tilePoint) {
|
|
||||||
|
|
||||||
var limit = this._getWrapTileNum();
|
|
||||||
|
|
||||||
// wrap tile coordinates
|
|
||||||
if (!this.options.continuousWorld && !this.options.noWrap) {
|
|
||||||
tilePoint.x = ((tilePoint.x % limit.x) + limit.x) % limit.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.tms) {
|
|
||||||
tilePoint.y = limit.y - tilePoint.y - 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_getWrapTileNum: function () {
|
|
||||||
var crs = this._map.options.crs,
|
|
||||||
size = crs.getSize(this._map.getZoom());
|
|
||||||
return size.divideBy(this._getTileSize())._floor();
|
|
||||||
},
|
|
||||||
|
|
||||||
_getTileSize: function () {
|
|
||||||
var map = this._map,
|
|
||||||
zoom = map.getZoom() + this.options.zoomOffset,
|
|
||||||
zoomN = this.options.maxNativeZoom,
|
|
||||||
tileSize = this.options.tileSize;
|
|
||||||
|
|
||||||
if (zoomN && zoom > zoomN) {
|
|
||||||
tileSize = Math.round(map.getZoomScale(zoom) / map.getZoomScale(zoomN) * tileSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tileSize;
|
|
||||||
},
|
|
||||||
|
|
||||||
_clearTileCaches: function() {
|
_clearTileCaches: function() {
|
||||||
var t, tile;
|
var t, tile;
|
||||||
for(t in this._tiles) {
|
for(t in this._tiles) {
|
||||||
@ -2580,6 +2560,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.renderer.applyFilters();
|
||||||
|
|
||||||
// prepare caches if the animation is not running
|
// prepare caches if the animation is not running
|
||||||
// don't cache if the key has just changed, this avoids to cache
|
// don't cache if the key has just changed, this avoids to cache
|
||||||
@ -2644,6 +2625,14 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
return new Date(time);
|
return new Date(time);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
timeToStep: function(timestamp) {
|
||||||
|
if (typeof timestamp === "Date") timestamp = timestamp.getTime();
|
||||||
|
if (!this.provider) return 0;
|
||||||
|
var times = this.provider.getKeySpan();
|
||||||
|
var step = (this.provider.getSteps() * (timestamp - times.start)) / (times.end - times.start);
|
||||||
|
return step;
|
||||||
|
},
|
||||||
|
|
||||||
getStep: function() {
|
getStep: function() {
|
||||||
return this.key;
|
return this.key;
|
||||||
},
|
},
|
||||||
@ -2660,7 +2649,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
* returns an object with the start and end times
|
* returns an object with the start and end times
|
||||||
*/
|
*/
|
||||||
getTimeSpan: function() {
|
getTimeSpan: function() {
|
||||||
var times = this.provider.getKeySpan();
|
return this.provider.getKeySpan();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2684,7 +2673,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
if (options.animationDuration) {
|
if (options.animationDuration) {
|
||||||
this.animator.duration(options.animationDuration);
|
this.animator.duration(options.animationDuration);
|
||||||
}
|
}
|
||||||
|
this._clearCaches();
|
||||||
this.redraw();
|
this.redraw();
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
@ -3845,6 +3834,8 @@ var Profiler = require('../profiler');
|
|||||||
this.options.tiler_domain = options.tiler_domain || 'cartodb.com';
|
this.options.tiler_domain = options.tiler_domain || 'cartodb.com';
|
||||||
this.options.tiler_port = options.tiler_port || 80;
|
this.options.tiler_port = options.tiler_port || 80;
|
||||||
|
|
||||||
|
this.options.coordinates_data_type = this.options.coordinates_data_type || Uint8Array;
|
||||||
|
|
||||||
if (this.options.data_aggregation) {
|
if (this.options.data_aggregation) {
|
||||||
this.options.cumulative = this.options.data_aggregation === 'cumulative';
|
this.options.cumulative = this.options.data_aggregation === 'cumulative';
|
||||||
}
|
}
|
||||||
@ -3870,8 +3861,8 @@ var Profiler = require('../profiler');
|
|||||||
*/
|
*/
|
||||||
proccessTile: function(rows, coord, zoom) {
|
proccessTile: function(rows, coord, zoom) {
|
||||||
var r;
|
var r;
|
||||||
var x = new Uint8Array(rows.length);
|
var x = new this.options.coordinates_data_type(rows.length);
|
||||||
var y = new Uint8Array(rows.length);
|
var y = new this.options.coordinates_data_type(rows.length);
|
||||||
|
|
||||||
var prof_mem = Profiler.metric('torque.provider.windshaft.mem');
|
var prof_mem = Profiler.metric('torque.provider.windshaft.mem');
|
||||||
var prof_point_count = Profiler.metric('torque.provider.windshaft.points');
|
var prof_point_count = Profiler.metric('torque.provider.windshaft.points');
|
||||||
@ -3915,13 +3906,7 @@ var Profiler = require('../profiler');
|
|||||||
for (var r = 0; r < rows.length; ++r) {
|
for (var r = 0; r < rows.length; ++r) {
|
||||||
var row = rows[r];
|
var row = rows[r];
|
||||||
x[r] = row.x__uint8 * this.options.resolution;
|
x[r] = row.x__uint8 * this.options.resolution;
|
||||||
// fix value when it's in the tile EDGE
|
|
||||||
// TODO: this should be fixed in SQL query
|
|
||||||
if (row.y__uint8 === -1) {
|
|
||||||
y[r] = 0;
|
|
||||||
} else {
|
|
||||||
y[r] = row.y__uint8 * this.options.resolution;
|
y[r] = row.y__uint8 * this.options.resolution;
|
||||||
}
|
|
||||||
|
|
||||||
var dates = row.dates__uint16;
|
var dates = row.dates__uint16;
|
||||||
var vals = row.vals__uint8;
|
var vals = row.vals__uint8;
|
||||||
@ -4100,10 +4085,12 @@ var Profiler = require('../profiler');
|
|||||||
var self = this;
|
var self = this;
|
||||||
var prof_fetch_time = Profiler.metric('torque.provider.windshaft.tile.fetch').start();
|
var prof_fetch_time = Profiler.metric('torque.provider.windshaft.tile.fetch').start();
|
||||||
var subdomains = this.options.subdomains || '0123';
|
var subdomains = this.options.subdomains || '0123';
|
||||||
var index = Math.abs(coord.corrected.x + coord.corrected.y) % subdomains.length;
|
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.templateUrl
|
||||||
.replace('{x}', coord.corrected.x)
|
.replace('{x}', corrected_x)
|
||||||
.replace('{y}', coord.corrected.y)
|
.replace('{y}', coord.y)
|
||||||
.replace('{z}', zoom)
|
.replace('{z}', zoom)
|
||||||
.replace('{s}', subdomains[index])
|
.replace('{s}', subdomains[index])
|
||||||
|
|
||||||
@ -4208,10 +4195,14 @@ var Profiler = require('../profiler');
|
|||||||
var host = this.options.dynamic_cdn ? this.url().replace('{s}', '0'): this._tilerHost();
|
var host = this.options.dynamic_cdn ? this.url().replace('{s}', '0'): this._tilerHost();
|
||||||
var url = host + "/api/v1/map";
|
var url = host + "/api/v1/map";
|
||||||
var named = this.options.named_map;
|
var named = this.options.named_map;
|
||||||
|
var allParams = {};
|
||||||
|
|
||||||
if(named) {
|
if(named) {
|
||||||
//tiles/template
|
//tiles/template
|
||||||
url = host + "/api/v1/map/named/" + named.name + "/jsonp";
|
url = host + "/api/v1/map/named/" + named.name + "/jsonp";
|
||||||
|
if(typeof named.params !== "undefined"){
|
||||||
|
layergroup = named.params;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
layergroup = {
|
layergroup = {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@ -4226,7 +4217,12 @@ var Profiler = require('../profiler');
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
var extra = this._extraParams(this.options.stat_tag ? { stat_tag: this.options.stat_tag }: {} );
|
|
||||||
|
if(this.options.stat_tag){
|
||||||
|
allParams["stat_tag"] = this.options.stat_tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
extra = this._extraParams(allParams);
|
||||||
|
|
||||||
// tiler needs map_key instead of api_key
|
// tiler needs map_key instead of api_key
|
||||||
// so replace it
|
// so replace it
|
||||||
@ -4339,9 +4335,13 @@ var Profiler = require('../profiler');
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSprite(ctx, img) {
|
function renderSprite(ctx, img, st) {
|
||||||
|
|
||||||
if(img.complete){
|
if(img.complete){
|
||||||
ctx.drawImage(img, -img.w/2, -img.h/2, img.w, img.h);
|
if (st['marker-fill-opacity'] !== undefined || st['marker-opacity'] !== undefined) {
|
||||||
|
ctx.globalAlpha = st['marker-fill-opacity'] || st['marker-opacity'];
|
||||||
|
}
|
||||||
|
ctx.drawImage(img, 0, 0, img.width, img.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4363,6 +4363,7 @@ var torque = require('../');
|
|||||||
var cartocss = require('./cartocss_render');
|
var cartocss = require('./cartocss_render');
|
||||||
var Profiler = require('../profiler');
|
var Profiler = require('../profiler');
|
||||||
var carto = global.carto || require('carto');
|
var carto = global.carto || require('carto');
|
||||||
|
var Filters = require('./torque_filters');
|
||||||
|
|
||||||
var TAU = Math.PI * 2;
|
var TAU = Math.PI * 2;
|
||||||
var DEFAULT_CARTOCSS = [
|
var DEFAULT_CARTOCSS = [
|
||||||
@ -4410,9 +4411,14 @@ var carto = global.carto || require('carto');
|
|||||||
this._ctx = canvas.getContext('2d');
|
this._ctx = canvas.getContext('2d');
|
||||||
this._sprites = []; // sprites per layer
|
this._sprites = []; // sprites per layer
|
||||||
this._shader = null;
|
this._shader = null;
|
||||||
|
this._icons = {};
|
||||||
|
this._iconsToLoad = 0;
|
||||||
|
this._filters = new Filters(this._canvas, {canvasClass: options.canvasClass});
|
||||||
this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS);
|
this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS);
|
||||||
this.TILE_SIZE = 256;
|
this.TILE_SIZE = 256;
|
||||||
this._icons = {};
|
this._style = null;
|
||||||
|
this._gradients = {};
|
||||||
|
|
||||||
this._forcePoints = false;
|
this._forcePoints = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4452,6 +4458,8 @@ var carto = global.carto || require('carto');
|
|||||||
this._sprites = [];
|
this._sprites = [];
|
||||||
this._shader = shader;
|
this._shader = shader;
|
||||||
this._Map = this._shader.getDefault().getStyle({}, { zoom: 0 });
|
this._Map = this._shader.getDefault().getStyle({}, { zoom: 0 });
|
||||||
|
var img_names = this._shader.getImageURLs();
|
||||||
|
this._preloadIcons(img_names);
|
||||||
},
|
},
|
||||||
|
|
||||||
clearSpriteCache: function() {
|
clearSpriteCache: function() {
|
||||||
@ -4468,6 +4476,9 @@ var carto = global.carto || require('carto');
|
|||||||
var st = shader.getStyle({
|
var st = shader.getStyle({
|
||||||
value: value
|
value: value
|
||||||
}, shaderVars);
|
}, shaderVars);
|
||||||
|
if(this._style === null || this._style !== st){
|
||||||
|
this._style = st;
|
||||||
|
}
|
||||||
|
|
||||||
var pointSize = st['marker-width'];
|
var pointSize = st['marker-width'];
|
||||||
if (!pointSize) {
|
if (!pointSize) {
|
||||||
@ -4479,22 +4490,29 @@ var carto = global.carto || require('carto');
|
|||||||
}
|
}
|
||||||
|
|
||||||
var canvas = this._createCanvas();
|
var canvas = this._createCanvas();
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
var markerFile = st["marker-file"] || st["point-file"];
|
||||||
|
var qualifiedUrl = markerFile && this._qualifyURL(markerFile);
|
||||||
|
|
||||||
|
if (qualifiedUrl && this._iconsToLoad <= 0 && this._icons[qualifiedUrl]) {
|
||||||
|
var img = this._icons[qualifiedUrl];
|
||||||
|
|
||||||
|
var dWidth = st['marker-width'] * 2 || img.width;
|
||||||
|
var dHeight = (st['marker-height'] || dWidth) * (img.width / img.height);
|
||||||
|
|
||||||
|
canvas.width = ctx.width = dWidth;
|
||||||
|
canvas.height = ctx.height = dHeight;
|
||||||
|
|
||||||
|
ctx.scale(dWidth/img.width, dHeight/img.height);
|
||||||
|
|
||||||
|
cartocss.renderSprite(ctx, img, st);
|
||||||
|
} else {
|
||||||
// take into account the exterior ring to calculate the size
|
// take into account the exterior ring to calculate the size
|
||||||
var canvasSize = (st['marker-line-width'] || 0) + pointSize*2;
|
var canvasSize = (st['marker-line-width'] || 0) + pointSize*2;
|
||||||
var ctx = canvas.getContext('2d');
|
|
||||||
var w = ctx.width = canvas.width = ctx.height = canvas.height = Math.ceil(canvasSize);
|
var w = ctx.width = canvas.width = ctx.height = canvas.height = Math.ceil(canvasSize);
|
||||||
ctx.translate(w/2, w/2);
|
ctx.translate(w/2, w/2);
|
||||||
|
|
||||||
var img_names = this._shader.getImageURLs();
|
|
||||||
this._preloadIcons(img_names);
|
|
||||||
if (img_names.length > 0 && this._icons.itemsToLoad === 0) {
|
|
||||||
var img_name = st["marker-file"] || st["point-file"];
|
|
||||||
var img = this._icons[img_name];
|
|
||||||
img.w = st['marker-width'] || img.width;
|
|
||||||
img.h = st['marker-width'] || st['marker-height'];
|
|
||||||
cartocss.renderSprite(ctx, img);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var mt = st['marker-type'];
|
var mt = st['marker-type'];
|
||||||
if (mt && mt === 'rectangle') {
|
if (mt && mt === 'rectangle') {
|
||||||
cartocss.renderRectangle(ctx, st);
|
cartocss.renderRectangle(ctx, st);
|
||||||
@ -4508,13 +4526,20 @@ var carto = global.carto || require('carto');
|
|||||||
i.src = canvas.toDataURL();
|
i.src = canvas.toDataURL();
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
},
|
},
|
||||||
|
|
||||||
//
|
//
|
||||||
// renders all the layers (and frames for each layer) from cartocss
|
// renders all the layers (and frames for each layer) from cartocss
|
||||||
//
|
//
|
||||||
renderTile: function(tile, key) {
|
renderTile: function(tile, key, callback) {
|
||||||
|
if (this._iconsToLoad > 0) {
|
||||||
|
this.on('allIconsLoaded', function() {
|
||||||
|
this.renderTile.apply(this, [tile, key, callback]);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
var prof = Profiler.metric('torque.renderer.point.renderLayers').start();
|
var prof = Profiler.metric('torque.renderer.point.renderLayers').start();
|
||||||
var layers = this._shader.getLayers();
|
var layers = this._shader.getLayers();
|
||||||
for(var i = 0, n = layers.length; i < n; ++i ) {
|
for(var i = 0, n = layers.length; i < n; ++i ) {
|
||||||
@ -4529,7 +4554,10 @@ var carto = global.carto || require('carto');
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prof.end(true);
|
prof.end(true);
|
||||||
|
|
||||||
|
return callback && callback(null);
|
||||||
},
|
},
|
||||||
|
|
||||||
_createCanvas: function() {
|
_createCanvas: function() {
|
||||||
@ -4544,6 +4572,31 @@ var carto = global.carto || require('carto');
|
|||||||
: new Image();
|
: new Image();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_setImageSrc: function(img, url, callback) {
|
||||||
|
if (this.options.setImageSrc) {
|
||||||
|
this.options.setImageSrc(img, url, callback);
|
||||||
|
} else {
|
||||||
|
img.onload = function(){
|
||||||
|
callback(null);
|
||||||
|
};
|
||||||
|
img.onerror = function(){
|
||||||
|
callback(new Error('Could not load image'));
|
||||||
|
};
|
||||||
|
img.src = url;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_qualifyURL: function(url) {
|
||||||
|
if (typeof this.options.qualifyURL !== "undefined"){
|
||||||
|
return this.options.qualifyURL(url);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
var a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
return a.href;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
//
|
//
|
||||||
// renders a tile in the canvas for key defined in
|
// renders a tile in the canvas for key defined in
|
||||||
// the torque tile
|
// the torque tile
|
||||||
@ -4563,6 +4616,7 @@ var carto = global.carto || require('carto');
|
|||||||
}
|
}
|
||||||
var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1)
|
var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1)
|
||||||
var activePixels = tile.timeCount[key];
|
var activePixels = tile.timeCount[key];
|
||||||
|
var anchor = this.options.resolution/2;
|
||||||
if (activePixels) {
|
if (activePixels) {
|
||||||
var pixelIndex = tile.timeIndex[key];
|
var pixelIndex = tile.timeIndex[key];
|
||||||
for(var p = 0; p < activePixels; ++p) {
|
for(var p = 0; p < activePixels; ++p) {
|
||||||
@ -4574,13 +4628,15 @@ var carto = global.carto || require('carto');
|
|||||||
sp = sprites[c] = this.generateSprite(shader, c, torque.extend({ zoom: tile.z, 'frame-offset': frame_offset }, shaderVars));
|
sp = sprites[c] = this.generateSprite(shader, c, torque.extend({ zoom: tile.z, 'frame-offset': frame_offset }, shaderVars));
|
||||||
}
|
}
|
||||||
if (sp) {
|
if (sp) {
|
||||||
var x = tile.x[posIdx]- (sp.width >> 1);
|
var x = tile.x[posIdx]- (sp.width >> 1) + anchor;
|
||||||
var y = tileMax - tile.y[posIdx]; // flip mercator
|
var y = tileMax - tile.y[posIdx] + anchor; // flip mercator
|
||||||
ctx.drawImage(sp, x, y - (sp.height >> 1));
|
ctx.drawImage(sp, x, y - (sp.height >> 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
prof.end(true);
|
prof.end(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -4652,37 +4708,98 @@ var carto = global.carto || require('carto');
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
_preloadIcons: function(img_names){
|
|
||||||
|
_preloadIcons: function(img_names) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (img_names.length > 0 && !this._forcePoints){
|
|
||||||
if (Object.keys(this._icons).length === 0){
|
if (img_names.length > 0 && !this._forcePoints) {
|
||||||
for (var i = 0; i<img_names.length; i++){
|
|
||||||
var new_img = this._createImage();
|
var qualifiedImageUrlSet = Object.keys(img_names.reduce(function(imgNamesMap, imgName) {
|
||||||
this._icons[img_names[i]] = null;
|
var qualifiedUrl = self._qualifyURL(imgName);
|
||||||
if (typeof self._icons.itemsToLoad === 'undefined'){
|
if (!self._icons[qualifiedUrl]) {
|
||||||
this._icons.itemsToLoad = img_names.length;
|
imgNamesMap[qualifiedUrl] = true;
|
||||||
}
|
}
|
||||||
new_img.onload = function(e){
|
return imgNamesMap;
|
||||||
self._icons[this.src] = this;
|
}, {}));
|
||||||
if (Object.keys(self._icons).length === img_names.length + 1){
|
|
||||||
self._icons.itemsToLoad--;
|
var filtered = self._shader.getLayers().some(function(layer) {
|
||||||
if (self._icons.itemsToLoad === 0){
|
return typeof layer.shader["image-filters"] !== "undefined";
|
||||||
|
});
|
||||||
|
|
||||||
|
this._iconsToLoad += qualifiedImageUrlSet.length;
|
||||||
|
|
||||||
|
qualifiedImageUrlSet.forEach(function(qualifiedImageUrl) {
|
||||||
|
self._icons[qualifiedImageUrl] = null;
|
||||||
|
|
||||||
|
var img = self._createImage();
|
||||||
|
|
||||||
|
if (filtered) {
|
||||||
|
img.crossOrigin = 'Anonymous';
|
||||||
|
}
|
||||||
|
|
||||||
|
self._setImageSrc(img, qualifiedImageUrl, function(err) {
|
||||||
|
if (err) {
|
||||||
|
self._forcePoints = true;
|
||||||
|
self.clearSpriteCache();
|
||||||
|
self._iconsToLoad = 0;
|
||||||
|
self.fire("allIconsLoaded");
|
||||||
|
if(filtered) {
|
||||||
|
console.info("Only CORS-enabled, or same domain image-files can be used in combination with image-filters");
|
||||||
|
}
|
||||||
|
console.error("Couldn't get marker-file " + qualifiedImageUrl);
|
||||||
|
} else {
|
||||||
|
self._icons[qualifiedImageUrl] = img;
|
||||||
|
self._iconsToLoad--;
|
||||||
|
|
||||||
|
if (self._iconsToLoad <= 0){
|
||||||
self.clearSpriteCache();
|
self.clearSpriteCache();
|
||||||
self.fire("allIconsLoaded");
|
self.fire("allIconsLoaded");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
new_img.onerror = function(){
|
});
|
||||||
self._forcePoints = true;
|
} else {
|
||||||
self.clearSpriteCache();
|
this.fire("allIconsLoaded");
|
||||||
console.error("Couldn't get marker-file " + this.src);
|
}
|
||||||
};
|
},
|
||||||
new_img.src = img_names[i];
|
|
||||||
|
applyFilters: function(){
|
||||||
|
if(this._style){
|
||||||
|
if(this._style['image-filters']){
|
||||||
|
function gradientKey(imf){
|
||||||
|
var hash = ""
|
||||||
|
for(var i = 0; i < imf.args.length; i++){
|
||||||
|
var rgb = imf.args[i].rgb;
|
||||||
|
hash += rgb[0] + ":" + rgb[1] + ":" + rgb[2];
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
var gradient = this._gradients[gradientKey(this._style['image-filters'])];
|
||||||
|
if(!gradient){
|
||||||
|
function componentToHex(c) {
|
||||||
|
var hex = c.toString(16);
|
||||||
|
return hex.length == 1 ? "0" + hex : hex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rgbToHex(r, g, b) {
|
||||||
|
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
|
||||||
}
|
}
|
||||||
}
|
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;
|
||||||
|
var rgb = colorize[i].rgb;
|
||||||
|
var formattedColor = rgbToHex(rgb[0], rgb[1], rgb[2]);
|
||||||
|
gradient[key] = formattedColor;
|
||||||
|
}
|
||||||
|
this._gradients[gradientKey(this._style['image-filters'])] = gradient;
|
||||||
|
}
|
||||||
|
this._filters.gradient(gradient);
|
||||||
|
this._filters.draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -4691,7 +4808,7 @@ var carto = global.carto || require('carto');
|
|||||||
module.exports = PointRenderer;
|
module.exports = PointRenderer;
|
||||||
|
|
||||||
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||||||
},{"../":10,"../profiler":17,"./cartocss_render":22,"carto":undefined}],25:[function(require,module,exports){
|
},{"../":10,"../profiler":17,"./cartocss_render":22,"./torque_filters":26,"carto":undefined}],25:[function(require,module,exports){
|
||||||
(function (global){
|
(function (global){
|
||||||
var carto = global.carto || require('carto');
|
var carto = global.carto || require('carto');
|
||||||
|
|
||||||
@ -4804,7 +4921,7 @@ var carto = global.carto || require('carto');
|
|||||||
// renders a tile in the canvas for key defined in
|
// renders a tile in the canvas for key defined in
|
||||||
// the torque tile
|
// the torque tile
|
||||||
//
|
//
|
||||||
renderTile: function(tile, key, px, py) {
|
renderTile: function(tile, key, callback) {
|
||||||
if(!this._canvas) return;
|
if(!this._canvas) return;
|
||||||
|
|
||||||
var res = this.options.resolution;
|
var res = this.options.resolution;
|
||||||
@ -4846,6 +4963,7 @@ var carto = global.carto || require('carto');
|
|||||||
//ctx.putImageData(imageData, 0, 0);
|
//ctx.putImageData(imageData, 0, 0);
|
||||||
}
|
}
|
||||||
//prof.end();
|
//prof.end();
|
||||||
|
return callback && callback(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4855,6 +4973,98 @@ module.exports = RectanbleRenderer;
|
|||||||
|
|
||||||
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||||||
},{"carto":undefined}],26:[function(require,module,exports){
|
},{"carto":undefined}],26:[function(require,module,exports){
|
||||||
|
/*
|
||||||
|
Based on simpleheat, a tiny JavaScript library for drawing heatmaps with Canvas,
|
||||||
|
by Vladimir Agafonkin
|
||||||
|
https://github.com/mourner/simpleheat
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function torque_filters(canvas, options) {
|
||||||
|
// jshint newcap: false, validthis: true
|
||||||
|
if (!(this instanceof torque_filters)) { return new torque_filters(canvas, options); }
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
this._canvas = canvas = typeof canvas === 'string' ? document.getElementById(canvas) : canvas;
|
||||||
|
|
||||||
|
this._ctx = canvas.getContext('2d');
|
||||||
|
this._width = canvas.width;
|
||||||
|
this._height = canvas.height;
|
||||||
|
|
||||||
|
this._max = 1;
|
||||||
|
this._data = [];
|
||||||
|
|
||||||
|
this.canvasClass = options.canvasClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
torque_filters.prototype = {
|
||||||
|
|
||||||
|
defaultGradient: {
|
||||||
|
0.4: 'blue',
|
||||||
|
0.6: 'cyan',
|
||||||
|
0.7: 'lime',
|
||||||
|
0.8: 'yellow',
|
||||||
|
1.0: 'red'
|
||||||
|
},
|
||||||
|
|
||||||
|
gradient: function (grad) {
|
||||||
|
// create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one
|
||||||
|
var canvas = this._createCanvas(),
|
||||||
|
ctx = canvas.getContext('2d'),
|
||||||
|
gradient = ctx.createLinearGradient(0, 0, 0, 256);
|
||||||
|
|
||||||
|
canvas.width = 1;
|
||||||
|
canvas.height = 256;
|
||||||
|
|
||||||
|
for (var i in grad) {
|
||||||
|
gradient.addColorStop(+i, grad[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillStyle = gradient;
|
||||||
|
ctx.fillRect(0, 0, 1, 256);
|
||||||
|
|
||||||
|
this._grad = ctx.getImageData(0, 0, 1, 256).data;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
draw: function () {
|
||||||
|
if (!this._grad) {
|
||||||
|
this.gradient(this.defaultGradient);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx = this._ctx;
|
||||||
|
var colored = ctx.getImageData(0, 0, this._canvas.width, this._canvas.height);
|
||||||
|
this._colorize(colored.data, this._grad);
|
||||||
|
ctx.putImageData(colored, 0, 0);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
_colorize: function (pixels, gradient) {
|
||||||
|
for (var i = 3, len = pixels.length, j; i < len; i += 4) {
|
||||||
|
j = pixels[i] * 4; // get gradient color from opacity value
|
||||||
|
|
||||||
|
if (j) {
|
||||||
|
pixels[i - 3] = gradient[j];
|
||||||
|
pixels[i - 2] = gradient[j + 1];
|
||||||
|
pixels[i - 1] = gradient[j + 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_createCanvas: function() {
|
||||||
|
return this.canvasClass
|
||||||
|
? new this.canvasClass()
|
||||||
|
: document.createElement('canvas');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = torque_filters;
|
||||||
|
|
||||||
|
},{}],27:[function(require,module,exports){
|
||||||
(function (global){
|
(function (global){
|
||||||
var torque = require('./core');
|
var torque = require('./core');
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@ One of two core classes for the Torque library - it is used to create an animate
|
|||||||
| user | string | ```null``` | CartoDB account name. Found from: http://accountname.cartodb.com|
|
| user | string | ```null``` | CartoDB account name. Found from: http://accountname.cartodb.com|
|
||||||
| table | string | ```null``` | CartoDB table name where data is found |
|
| table | string | ```null``` | CartoDB table name where data is found |
|
||||||
| sql | string | ```null``` | SQL query to be performed to fetch the data. You must use this param or table, not at the same time |
|
| sql | string | ```null``` | SQL query to be performed to fetch the data. You must use this param or table, not at the same time |
|
||||||
|
| cartocss | string | ```null``` | CartoCSS style for this map |
|
||||||
|
| loop | boolean | ```true``` | If ```false```, the animation is paused when it reaches the last frame |
|
||||||
|
|
||||||
|
|
||||||
### Time methods
|
### Time methods
|
||||||
|
@ -46,6 +46,9 @@ var cancelAnimationFrame = global.cancelAnimationFrame
|
|||||||
this.running = true;
|
this.running = true;
|
||||||
requestAnimationFrame(this._tick);
|
requestAnimationFrame(this._tick);
|
||||||
this.options.onStart && this.options.onStart();
|
this.options.onStart && this.options.onStart();
|
||||||
|
if(this.options.steps === 1){
|
||||||
|
this.running = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
isRunning: function() {
|
isRunning: function() {
|
||||||
@ -80,6 +83,7 @@ var cancelAnimationFrame = global.cancelAnimationFrame
|
|||||||
this.range = torque.math.linear(0, this.options.steps);
|
this.range = torque.math.linear(0, this.options.steps);
|
||||||
this.rangeInv = this.range.invert();
|
this.rangeInv = this.range.invert();
|
||||||
this.time(this._time);
|
this.time(this._time);
|
||||||
|
this.start();
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -118,9 +122,12 @@ var cancelAnimationFrame = global.cancelAnimationFrame
|
|||||||
this._t0 = t1;
|
this._t0 = t1;
|
||||||
this._time += delta;
|
this._time += delta;
|
||||||
if(this.step() >= this.options.steps) {
|
if(this.step() >= this.options.steps) {
|
||||||
this._time = 0;
|
|
||||||
if(!this.options.loop){
|
if(!this.options.loop){
|
||||||
this.stop();
|
// set time to max time
|
||||||
|
this.time(this.options.animationDuration);
|
||||||
|
this.pause();
|
||||||
|
} else {
|
||||||
|
this._time = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(this.running) {
|
if(this.running) {
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
var types = {
|
var types = {
|
||||||
Uint8Array: typeof(global['Uint8Array']) !== 'undefined' ? global.Uint8Array : Array,
|
Uint8Array: typeof(global['Uint8Array']) !== 'undefined' ? global.Uint8Array : Array,
|
||||||
Uint32Array: typeof(global['Uint32Array']) !== 'undefined' ? global.Uint32Array : Array,
|
Uint32Array: typeof(global['Uint32Array']) !== 'undefined' ? global.Uint32Array : Array,
|
||||||
|
Int16Array: typeof(global['Int16Array']) !== 'undefined' ? global.Int16Array : Array,
|
||||||
Int32Array: typeof(global['Int32Array']) !== 'undefined' ? global.Int32Array: Array
|
Int32Array: typeof(global['Int32Array']) !== 'undefined' ? global.Int32Array: Array
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
|
|
||||||
providers: {
|
providers: {
|
||||||
'sql_api': torque.providers.json,
|
'sql_api': torque.providers.json,
|
||||||
'url_template': torque.providers.jsonarray,
|
'url_template': torque.providers.JsonArray,
|
||||||
'windshaft': torque.providers.windshaft
|
'windshaft': torque.providers.windshaft
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -189,9 +189,10 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
if (tile) {
|
if (tile) {
|
||||||
pos = this.getTilePos(tile.coord);
|
pos = this.getTilePos(tile.coord);
|
||||||
ctx.setTransform(1, 0, 0, 1, pos.x, pos.y);
|
ctx.setTransform(1, 0, 0, 1, pos.x, pos.y);
|
||||||
this.renderer.renderTile(tile, this.key, pos.x, pos.y);
|
this.renderer.renderTile(tile, this.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.renderer.applyFilters();
|
||||||
},
|
},
|
||||||
|
|
||||||
getActivePointsBBox: function(step) {
|
getActivePointsBBox: function(step) {
|
||||||
|
@ -10,7 +10,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
|
|
||||||
providers: {
|
providers: {
|
||||||
'sql_api': torque.providers.json,
|
'sql_api': torque.providers.json,
|
||||||
'url_template': torque.providers.jsonarray,
|
'url_template': torque.providers.JsonArray,
|
||||||
'windshaft': torque.providers.windshaft
|
'windshaft': torque.providers.windshaft
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
this.options.tiler_domain = options.tiler_domain || 'cartodb.com';
|
this.options.tiler_domain = options.tiler_domain || 'cartodb.com';
|
||||||
this.options.tiler_port = options.tiler_port || 80;
|
this.options.tiler_port = options.tiler_port || 80;
|
||||||
|
|
||||||
|
this.options.coordinates_data_type = this.options.coordinates_data_type || Uint8Array;
|
||||||
|
|
||||||
if (this.options.data_aggregation) {
|
if (this.options.data_aggregation) {
|
||||||
this.options.cumulative = this.options.data_aggregation === 'cumulative';
|
this.options.cumulative = this.options.data_aggregation === 'cumulative';
|
||||||
}
|
}
|
||||||
@ -51,8 +53,8 @@
|
|||||||
*/
|
*/
|
||||||
proccessTile: function(rows, coord, zoom) {
|
proccessTile: function(rows, coord, zoom) {
|
||||||
var r;
|
var r;
|
||||||
var x = new Uint8Array(rows.length);
|
var x = new this.options.coordinates_data_type(rows.length);
|
||||||
var y = new Uint8Array(rows.length);
|
var y = new this.options.coordinates_data_type(rows.length);
|
||||||
|
|
||||||
var prof_mem = Profiler.metric('torque.provider.windshaft.mem');
|
var prof_mem = Profiler.metric('torque.provider.windshaft.mem');
|
||||||
var prof_point_count = Profiler.metric('torque.provider.windshaft.points');
|
var prof_point_count = Profiler.metric('torque.provider.windshaft.points');
|
||||||
@ -96,13 +98,7 @@
|
|||||||
for (var r = 0; r < rows.length; ++r) {
|
for (var r = 0; r < rows.length; ++r) {
|
||||||
var row = rows[r];
|
var row = rows[r];
|
||||||
x[r] = row.x__uint8 * this.options.resolution;
|
x[r] = row.x__uint8 * this.options.resolution;
|
||||||
// fix value when it's in the tile EDGE
|
|
||||||
// TODO: this should be fixed in SQL query
|
|
||||||
if (row.y__uint8 === -1) {
|
|
||||||
y[r] = 0;
|
|
||||||
} else {
|
|
||||||
y[r] = row.y__uint8 * this.options.resolution;
|
y[r] = row.y__uint8 * this.options.resolution;
|
||||||
}
|
|
||||||
|
|
||||||
var dates = row.dates__uint16;
|
var dates = row.dates__uint16;
|
||||||
var vals = row.vals__uint8;
|
var vals = row.vals__uint8;
|
||||||
|
@ -77,7 +77,7 @@
|
|||||||
if (st['marker-fill-opacity'] !== undefined || st['marker-opacity'] !== undefined) {
|
if (st['marker-fill-opacity'] !== undefined || st['marker-opacity'] !== undefined) {
|
||||||
ctx.globalAlpha = st['marker-fill-opacity'] || st['marker-opacity'];
|
ctx.globalAlpha = st['marker-fill-opacity'] || st['marker-opacity'];
|
||||||
}
|
}
|
||||||
ctx.drawImage(img, -img.w, -img.h, img.w*2, img.h*2);
|
ctx.drawImage(img, 0, 0, img.width, img.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ var torque = require('../');
|
|||||||
var cartocss = require('./cartocss_render');
|
var cartocss = require('./cartocss_render');
|
||||||
var Profiler = require('../profiler');
|
var Profiler = require('../profiler');
|
||||||
var carto = global.carto || require('carto');
|
var carto = global.carto || require('carto');
|
||||||
var filters = require('./torque_filters');
|
var Filters = require('./torque_filters');
|
||||||
|
|
||||||
var TAU = Math.PI * 2;
|
var TAU = Math.PI * 2;
|
||||||
var DEFAULT_CARTOCSS = [
|
var DEFAULT_CARTOCSS = [
|
||||||
@ -51,7 +51,8 @@ var filters = require('./torque_filters');
|
|||||||
this._sprites = []; // sprites per layer
|
this._sprites = []; // sprites per layer
|
||||||
this._shader = null;
|
this._shader = null;
|
||||||
this._icons = {};
|
this._icons = {};
|
||||||
this._filters = filters(this._canvas);
|
this._iconsToLoad = 0;
|
||||||
|
this._filters = new Filters(this._canvas, {canvasClass: options.canvasClass});
|
||||||
this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS);
|
this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS);
|
||||||
this.TILE_SIZE = 256;
|
this.TILE_SIZE = 256;
|
||||||
this._style = null;
|
this._style = null;
|
||||||
@ -128,20 +129,29 @@ var filters = require('./torque_filters');
|
|||||||
}
|
}
|
||||||
|
|
||||||
var canvas = this._createCanvas();
|
var canvas = this._createCanvas();
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
var markerFile = st["marker-file"] || st["point-file"];
|
||||||
|
var qualifiedUrl = markerFile && this._qualifyURL(markerFile);
|
||||||
|
|
||||||
|
if (qualifiedUrl && this._iconsToLoad <= 0 && this._icons[qualifiedUrl]) {
|
||||||
|
var img = this._icons[qualifiedUrl];
|
||||||
|
|
||||||
|
var dWidth = st['marker-width'] * 2 || img.width;
|
||||||
|
var dHeight = (st['marker-height'] || dWidth) * (img.width / img.height);
|
||||||
|
|
||||||
|
canvas.width = ctx.width = dWidth;
|
||||||
|
canvas.height = ctx.height = dHeight;
|
||||||
|
|
||||||
|
ctx.scale(dWidth/img.width, dHeight/img.height);
|
||||||
|
|
||||||
|
cartocss.renderSprite(ctx, img, st);
|
||||||
|
} else {
|
||||||
// take into account the exterior ring to calculate the size
|
// take into account the exterior ring to calculate the size
|
||||||
var canvasSize = (st['marker-line-width'] || 0) + pointSize*2;
|
var canvasSize = (st['marker-line-width'] || 0) + pointSize*2;
|
||||||
var ctx = canvas.getContext('2d');
|
|
||||||
var w = ctx.width = canvas.width = ctx.height = canvas.height = Math.ceil(canvasSize);
|
var w = ctx.width = canvas.width = ctx.height = canvas.height = Math.ceil(canvasSize);
|
||||||
ctx.translate(w/2, w/2);
|
ctx.translate(w/2, w/2);
|
||||||
|
|
||||||
var img_name = this._qualifyURL(st["marker-file"] || st["point-file"]);
|
|
||||||
if (img_name && this._icons.itemsToLoad <= 0) {
|
|
||||||
var img = this._icons[img_name];
|
|
||||||
img.w = st['marker-width'] || img.width;
|
|
||||||
img.h = st['marker-width'] || st['marker-height'];
|
|
||||||
cartocss.renderSprite(ctx, img, st);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var mt = st['marker-type'];
|
var mt = st['marker-type'];
|
||||||
if (mt && mt === 'rectangle') {
|
if (mt && mt === 'rectangle') {
|
||||||
cartocss.renderRectangle(ctx, st);
|
cartocss.renderRectangle(ctx, st);
|
||||||
@ -162,7 +172,13 @@ var filters = require('./torque_filters');
|
|||||||
//
|
//
|
||||||
// renders all the layers (and frames for each layer) from cartocss
|
// renders all the layers (and frames for each layer) from cartocss
|
||||||
//
|
//
|
||||||
renderTile: function(tile, key) {
|
renderTile: function(tile, key, callback) {
|
||||||
|
if (this._iconsToLoad > 0) {
|
||||||
|
this.on('allIconsLoaded', function() {
|
||||||
|
this.renderTile.apply(this, [tile, key, callback]);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
var prof = Profiler.metric('torque.renderer.point.renderLayers').start();
|
var prof = Profiler.metric('torque.renderer.point.renderLayers').start();
|
||||||
var layers = this._shader.getLayers();
|
var layers = this._shader.getLayers();
|
||||||
for(var i = 0, n = layers.length; i < n; ++i ) {
|
for(var i = 0, n = layers.length; i < n; ++i ) {
|
||||||
@ -179,6 +195,8 @@ var filters = require('./torque_filters');
|
|||||||
}
|
}
|
||||||
|
|
||||||
prof.end(true);
|
prof.end(true);
|
||||||
|
|
||||||
|
return callback && callback(null);
|
||||||
},
|
},
|
||||||
|
|
||||||
_createCanvas: function() {
|
_createCanvas: function() {
|
||||||
@ -192,6 +210,21 @@ var filters = require('./torque_filters');
|
|||||||
? new this.options.imageClass()
|
? new this.options.imageClass()
|
||||||
: new Image();
|
: new Image();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_setImageSrc: function(img, url, callback) {
|
||||||
|
if (this.options.setImageSrc) {
|
||||||
|
this.options.setImageSrc(img, url, callback);
|
||||||
|
} else {
|
||||||
|
img.onload = function(){
|
||||||
|
callback(null);
|
||||||
|
};
|
||||||
|
img.onerror = function(){
|
||||||
|
callback(new Error('Could not load image'));
|
||||||
|
};
|
||||||
|
img.src = url;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_qualifyURL: function(url) {
|
_qualifyURL: function(url) {
|
||||||
if (typeof this.options.qualifyURL !== "undefined"){
|
if (typeof this.options.qualifyURL !== "undefined"){
|
||||||
return this.options.qualifyURL(url);
|
return this.options.qualifyURL(url);
|
||||||
@ -222,6 +255,7 @@ var filters = require('./torque_filters');
|
|||||||
}
|
}
|
||||||
var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1)
|
var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1)
|
||||||
var activePixels = tile.timeCount[key];
|
var activePixels = tile.timeCount[key];
|
||||||
|
var anchor = this.options.resolution/2;
|
||||||
if (activePixels) {
|
if (activePixels) {
|
||||||
var pixelIndex = tile.timeIndex[key];
|
var pixelIndex = tile.timeIndex[key];
|
||||||
for(var p = 0; p < activePixels; ++p) {
|
for(var p = 0; p < activePixels; ++p) {
|
||||||
@ -233,8 +267,8 @@ var filters = require('./torque_filters');
|
|||||||
sp = sprites[c] = this.generateSprite(shader, c, torque.extend({ zoom: tile.z, 'frame-offset': frame_offset }, shaderVars));
|
sp = sprites[c] = this.generateSprite(shader, c, torque.extend({ zoom: tile.z, 'frame-offset': frame_offset }, shaderVars));
|
||||||
}
|
}
|
||||||
if (sp) {
|
if (sp) {
|
||||||
var x = tile.x[posIdx]- (sp.width >> 1);
|
var x = tile.x[posIdx]- (sp.width >> 1) + anchor;
|
||||||
var y = tileMax - tile.y[posIdx]; // flip mercator
|
var y = tileMax - tile.y[posIdx] + anchor; // flip mercator
|
||||||
ctx.drawImage(sp, x, y - (sp.height >> 1));
|
ctx.drawImage(sp, x, y - (sp.height >> 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,44 +347,61 @@ var filters = require('./torque_filters');
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
_preloadIcons: function(img_names){
|
|
||||||
|
_preloadIcons: function(img_names) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this._icons = {};
|
|
||||||
if (img_names.length > 0 && !this._forcePoints){
|
if (img_names.length > 0 && !this._forcePoints) {
|
||||||
for (var i = 0; i<img_names.length; i++){
|
|
||||||
var new_img = this._createImage();
|
var qualifiedImageUrlSet = Object.keys(img_names.reduce(function(imgNamesMap, imgName) {
|
||||||
this._icons[this._qualifyURL(img_names[i])] = null;
|
var qualifiedUrl = self._qualifyURL(imgName);
|
||||||
if (typeof self._icons.itemsToLoad === 'undefined'){
|
if (!self._icons[qualifiedUrl]) {
|
||||||
this._icons.itemsToLoad = img_names.length;
|
imgNamesMap[qualifiedUrl] = true;
|
||||||
}
|
}
|
||||||
var filtered = self._shader.getLayers().some(function(layer){return typeof layer.shader["image-filters"] !== "undefined"});
|
return imgNamesMap;
|
||||||
if (filtered){
|
}, {}));
|
||||||
new_img.crossOrigin = 'Anonymous';
|
|
||||||
|
var filtered = self._shader.getLayers().some(function(layer) {
|
||||||
|
return typeof layer.shader["image-filters"] !== "undefined";
|
||||||
|
});
|
||||||
|
|
||||||
|
this._iconsToLoad += qualifiedImageUrlSet.length;
|
||||||
|
|
||||||
|
qualifiedImageUrlSet.forEach(function(qualifiedImageUrl) {
|
||||||
|
self._icons[qualifiedImageUrl] = null;
|
||||||
|
|
||||||
|
var img = self._createImage();
|
||||||
|
|
||||||
|
if (filtered) {
|
||||||
|
img.crossOrigin = 'Anonymous';
|
||||||
}
|
}
|
||||||
new_img.onload = function(e){
|
|
||||||
self._icons[this.src] = this;
|
self._setImageSrc(img, qualifiedImageUrl, function(err) {
|
||||||
if (Object.keys(self._icons).length === img_names.length + 1){
|
if (err) {
|
||||||
self._icons.itemsToLoad--;
|
self._forcePoints = true;
|
||||||
if (self._icons.itemsToLoad <= 0){
|
self.clearSpriteCache();
|
||||||
|
self._iconsToLoad = 0;
|
||||||
|
self.fire("allIconsLoaded");
|
||||||
|
if(filtered) {
|
||||||
|
console.info("Only CORS-enabled, or same domain image-files can be used in combination with image-filters");
|
||||||
|
}
|
||||||
|
console.error("Couldn't get marker-file " + qualifiedImageUrl);
|
||||||
|
} else {
|
||||||
|
self._icons[qualifiedImageUrl] = img;
|
||||||
|
self._iconsToLoad--;
|
||||||
|
|
||||||
|
if (self._iconsToLoad <= 0){
|
||||||
self.clearSpriteCache();
|
self.clearSpriteCache();
|
||||||
self.fire("allIconsLoaded");
|
self.fire("allIconsLoaded");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
new_img.onerror = function(){
|
});
|
||||||
self._forcePoints = true;
|
} else {
|
||||||
self.clearSpriteCache();
|
this.fire("allIconsLoaded");
|
||||||
if(filtered){
|
|
||||||
console.info("Only CORS-enabled, or same domain image-files can be used in combination with image-filters");
|
|
||||||
}
|
}
|
||||||
console.error("Couldn't get marker-file " + this.src);
|
|
||||||
};
|
|
||||||
this.itemsToLoad++;
|
|
||||||
new_img.src = img_names[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
applyFilters: function(){
|
applyFilters: function(){
|
||||||
if(this._style){
|
if(this._style){
|
||||||
if(this._style['image-filters']){
|
if(this._style['image-filters']){
|
||||||
|
@ -109,7 +109,7 @@ var carto = global.carto || require('carto');
|
|||||||
// renders a tile in the canvas for key defined in
|
// renders a tile in the canvas for key defined in
|
||||||
// the torque tile
|
// the torque tile
|
||||||
//
|
//
|
||||||
renderTile: function(tile, key, px, py) {
|
renderTile: function(tile, key, callback) {
|
||||||
if(!this._canvas) return;
|
if(!this._canvas) return;
|
||||||
|
|
||||||
var res = this.options.resolution;
|
var res = this.options.resolution;
|
||||||
@ -151,6 +151,7 @@ var carto = global.carto || require('carto');
|
|||||||
//ctx.putImageData(imageData, 0, 0);
|
//ctx.putImageData(imageData, 0, 0);
|
||||||
}
|
}
|
||||||
//prof.end();
|
//prof.end();
|
||||||
|
return callback && callback(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,9 +6,11 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function torque_filters(canvas) {
|
function torque_filters(canvas, options) {
|
||||||
// jshint newcap: false, validthis: true
|
// jshint newcap: false, validthis: true
|
||||||
if (!(this instanceof torque_filters)) { return new torque_filters(canvas); }
|
if (!(this instanceof torque_filters)) { return new torque_filters(canvas, options); }
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
this._canvas = canvas = typeof canvas === 'string' ? document.getElementById(canvas) : canvas;
|
this._canvas = canvas = typeof canvas === 'string' ? document.getElementById(canvas) : canvas;
|
||||||
|
|
||||||
@ -18,6 +20,8 @@ function torque_filters(canvas) {
|
|||||||
|
|
||||||
this._max = 1;
|
this._max = 1;
|
||||||
this._data = [];
|
this._data = [];
|
||||||
|
|
||||||
|
this.canvasClass = options.canvasClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
torque_filters.prototype = {
|
torque_filters.prototype = {
|
||||||
@ -32,7 +36,7 @@ torque_filters.prototype = {
|
|||||||
|
|
||||||
gradient: function (grad) {
|
gradient: function (grad) {
|
||||||
// create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one
|
// create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one
|
||||||
var canvas = document.createElement('canvas'),
|
var canvas = this._createCanvas(),
|
||||||
ctx = canvas.getContext('2d'),
|
ctx = canvas.getContext('2d'),
|
||||||
gradient = ctx.createLinearGradient(0, 0, 0, 256);
|
gradient = ctx.createLinearGradient(0, 0, 0, 256);
|
||||||
|
|
||||||
@ -40,7 +44,7 @@ torque_filters.prototype = {
|
|||||||
canvas.height = 256;
|
canvas.height = 256;
|
||||||
|
|
||||||
for (var i in grad) {
|
for (var i in grad) {
|
||||||
gradient.addColorStop(i, grad[i]);
|
gradient.addColorStop(+i, grad[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.fillStyle = gradient;
|
ctx.fillStyle = gradient;
|
||||||
@ -74,6 +78,12 @@ torque_filters.prototype = {
|
|||||||
pixels[i - 1] = gradient[j + 2];
|
pixels[i - 1] = gradient[j + 2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_createCanvas: function() {
|
||||||
|
return this.canvasClass
|
||||||
|
? new this.canvasClass()
|
||||||
|
: document.createElement('canvas');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "torque.js",
|
"name": "torque.js",
|
||||||
"version": "2.10.0",
|
"version": "2.11.1",
|
||||||
"description": "Torque javascript library",
|
"description": "Torque javascript library",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -29,13 +29,18 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"leaflet": "0.7.3",
|
"leaflet": "0.7.3",
|
||||||
|
"underscore": "^1.6.0",
|
||||||
"node-qunit-phantomjs": "^1.0.0",
|
"node-qunit-phantomjs": "^1.0.0",
|
||||||
"browserify": "^7.0.0",
|
"browserify": "^7.0.0",
|
||||||
|
"mapnik": "https://github.com/CartoDB/node-mapnik/tarball/1.4.15-cdb1",
|
||||||
|
"canvas": "~1.2.1",
|
||||||
|
"request": "^2.53.0",
|
||||||
|
"qunit": "~0.7.5",
|
||||||
"qunitjs": "1.x",
|
"qunitjs": "1.x",
|
||||||
"uglify-js": "1.3.3"
|
"uglify-js": "1.3.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "make prepare-test-suite && ./node_modules/node-qunit-phantomjs/bin/node-qunit-phantomjs test/suite.html"
|
"test": "make test-all"
|
||||||
},
|
},
|
||||||
"main": "./lib/torque/index.js"
|
"main": "./lib/torque/index.js"
|
||||||
}
|
}
|
||||||
|
22
test/acceptance/example.js
Normal file
22
test/acceptance/example.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
var Canvas = require('canvas');
|
||||||
|
var image = require('../support/image');
|
||||||
|
|
||||||
|
QUnit.module('example');
|
||||||
|
|
||||||
|
test('reference test with canvas', function() {
|
||||||
|
var circleRadius = 20;
|
||||||
|
var canvasSize = circleRadius * 2 + 2;
|
||||||
|
var canvas = new Canvas(canvasSize, canvasSize);
|
||||||
|
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(circleRadius + 1, circleRadius + 1, circleRadius, 0, Math.PI * 2, true);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
var imageDiff = image.compare(canvas.toBuffer(), 'canvas_basic_reference.png');
|
||||||
|
|
||||||
|
equal(imageDiff, 0);
|
||||||
|
});
|
||||||
|
|
66
test/acceptance/renderer/point.js
Normal file
66
test/acceptance/renderer/point.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
var pointRenderer = require('../../support/point_renderer');
|
||||||
|
var image = require('../../support/image');
|
||||||
|
|
||||||
|
QUnit.module('renderer/point');
|
||||||
|
|
||||||
|
var IMAGE_DIFF_TOLERANCE = 4 / 100;
|
||||||
|
|
||||||
|
asyncTest('navy example', function(assert) {
|
||||||
|
var cartocss = [
|
||||||
|
'Map {',
|
||||||
|
' -torque-time-attribute: "date";',
|
||||||
|
' -torque-aggregation-function: "count(cartodb_id)";',
|
||||||
|
' -torque-frame-count: 760;',
|
||||||
|
' -torque-animation-duration: 15;',
|
||||||
|
' -torque-resolution: 2',
|
||||||
|
'}',
|
||||||
|
'#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');
|
||||||
|
|
||||||
|
var step = 300;
|
||||||
|
|
||||||
|
pointRenderer.getTile('default_navy_3-3-2.torque.json', cartocss, 3, 3, 2, step, function(err, canvas) {
|
||||||
|
assert.ok(!err, 'no error while getting tile');
|
||||||
|
var imageDiff = image.compare(canvas.toBuffer(), 'default_navy_3-3-2.png');
|
||||||
|
assert.ok(imageDiff < IMAGE_DIFF_TOLERANCE, 'navy tile is ok');
|
||||||
|
QUnit.start();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('basic heatmap', function(assert) {
|
||||||
|
var cartocss = [
|
||||||
|
'Map {',
|
||||||
|
' -torque-time-attribute: "date";',
|
||||||
|
' -torque-aggregation-function: "count(cartodb_id)";',
|
||||||
|
' -torque-frame-count: 1;',
|
||||||
|
' -torque-resolution: 1',
|
||||||
|
'}',
|
||||||
|
'#layer {',
|
||||||
|
' marker-width: 4;',
|
||||||
|
' image-filters: colorize-alpha(blue, cyan, lightgreen, yellow , orange, red);',
|
||||||
|
' marker-file: url(http://s3.amazonaws.com/com.cartodb.assets.static/alphamarker.png);',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
var step = 0;
|
||||||
|
|
||||||
|
pointRenderer.getTile('heatmap_navy_3-2-3.torque.json', cartocss, 3, 2, 3, step, function(err, canvas) {
|
||||||
|
assert.ok(!err, 'no error while getting tile');
|
||||||
|
var imageDiff = image.compare(canvas.toBuffer(), 'heatmap_navy_3-2-3.png');
|
||||||
|
assert.ok(imageDiff < IMAGE_DIFF_TOLERANCE, 'heatmap tile is ok');
|
||||||
|
QUnit.start();
|
||||||
|
});
|
||||||
|
});
|
BIN
test/fixtures/image/canvas_basic_reference.png
vendored
Normal file
BIN
test/fixtures/image/canvas_basic_reference.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 507 B |
BIN
test/fixtures/image/default_navy_3-3-2.png
vendored
Normal file
BIN
test/fixtures/image/default_navy_3-3-2.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
BIN
test/fixtures/image/heatmap_navy_3-2-3.png
vendored
Normal file
BIN
test/fixtures/image/heatmap_navy_3-2-3.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
1
test/fixtures/json/default_navy_3-3-2.torque.json
vendored
Normal file
1
test/fixtures/json/default_navy_3-3-2.torque.json
vendored
Normal file
File diff suppressed because one or more lines are too long
1
test/fixtures/json/heatmap_navy_3-2-3.torque.json
vendored
Normal file
1
test/fixtures/json/heatmap_navy_3-2-3.torque.json
vendored
Normal file
File diff suppressed because one or more lines are too long
1
test/results/.gitignore
vendored
Normal file
1
test/results/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.png
|
19
test/support/image.js
Normal file
19
test/support/image.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
var mapnik = require('mapnik');
|
||||||
|
|
||||||
|
function compare(buffer, fixtureRelPath) {
|
||||||
|
save(__dirname + '/../results/' + fixtureRelPath, buffer);
|
||||||
|
|
||||||
|
var img = new mapnik.Image.fromBytesSync(buffer);
|
||||||
|
var reference = new mapnik.Image.openSync(__dirname + '/../fixtures/image/' + fixtureRelPath);
|
||||||
|
return img.compare(reference) / (reference.width() * reference.height());
|
||||||
|
}
|
||||||
|
|
||||||
|
function save(path, buffer) {
|
||||||
|
var img = new mapnik.Image.fromBytesSync(buffer);
|
||||||
|
img.save(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
compare: compare,
|
||||||
|
save: save
|
||||||
|
};
|
59
test/support/point_renderer.js
Normal file
59
test/support/point_renderer.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
var Canvas = require('canvas');
|
||||||
|
var request = require('request');
|
||||||
|
var _ = require('underscore');
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
var torque = require('../../lib/torque/index');
|
||||||
|
|
||||||
|
|
||||||
|
function getTile(jsonRelPath, cartocss, z, x, y, step, callback) {
|
||||||
|
step = step || 0;
|
||||||
|
|
||||||
|
var cartoCssOptions = torque.common.TorqueLayer.optionsFromCartoCSS(cartocss);
|
||||||
|
|
||||||
|
var provider = new torque.providers.windshaft(_.extend({ no_fetch_map: true }, cartoCssOptions));
|
||||||
|
var rendererOptions = _.extend({cartocss: cartocss}, cartoCssOptions, {
|
||||||
|
canvasClass: Canvas,
|
||||||
|
imageClass: Canvas.Image,
|
||||||
|
setImageSrc: function(img, url, callback) {
|
||||||
|
var requestOpts = {
|
||||||
|
url: url,
|
||||||
|
method: 'GET',
|
||||||
|
encoding: null
|
||||||
|
};
|
||||||
|
request(requestOpts, function (err, response, body) {
|
||||||
|
if (!err && response.statusCode === 200) {
|
||||||
|
img.onload = function() {
|
||||||
|
callback(null);
|
||||||
|
};
|
||||||
|
img.onerror = function() {
|
||||||
|
callback(new Error('Could not load marker-file image: ' + url));
|
||||||
|
};
|
||||||
|
img.src = body;
|
||||||
|
} else {
|
||||||
|
callback(new Error('Could not load marker-file image: ' + url));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
qualifyURL: function(url) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var rows = JSON.parse(fs.readFileSync(__dirname + '/../fixtures/json/' + jsonRelPath));
|
||||||
|
|
||||||
|
var canvas = new Canvas(256, 256);
|
||||||
|
var pointRenderer = new torque.renderer.Point(canvas, rendererOptions);
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
getTile: getTile
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user