canvas test

This commit is contained in:
javi 2014-05-23 18:48:55 +02:00
parent 3452e279d1
commit eac198f9cf
7 changed files with 1221 additions and 73 deletions

View File

@ -23,7 +23,7 @@
'-torque-aggregation-function: "count(cartodb_id)";', '-torque-aggregation-function: "count(cartodb_id)";',
'-torque-frame-count: 760;', '-torque-frame-count: 760;',
'-torque-animation-duration: 15;', '-torque-animation-duration: 15;',
'-torque-resolution: 2', '-torque-resolution: 1',
'}', '}',
'#layer {', '#layer {',
' marker-width: 3;', ' marker-width: 3;',
@ -57,7 +57,9 @@
table : 'ow', table : 'ow',
cartocss: CARTOCSS cartocss: CARTOCSS
}); });
torqueLayer.addTo(map); torqueLayer.addTo(map);
//torqueLayer.setStep(30);
torqueLayer.play() torqueLayer.play()
</script> </script>
</body> </body>

View File

@ -18,8 +18,7 @@ L.CanvasLayer = L.Class.extend({
opacity: 1, opacity: 1,
unloadInvisibleTiles: L.Browser.mobile, unloadInvisibleTiles: L.Browser.mobile,
updateWhenIdle: L.Browser.mobile, updateWhenIdle: L.Browser.mobile,
tileLoader: false, // installs tile loading events tileLoader: true// installs tile loading events
zoomAnimation: true
}, },
initialize: function (options) { initialize: function (options) {
@ -30,10 +29,7 @@ L.CanvasLayer = L.Class.extend({
L.Util.setOptions(this, options); L.Util.setOptions(this, options);
this._canvas = this._createCanvas(); this._canvas = this._createCanvas();
// backCanvas for zoom animation // backCanvas for zoom animation
if (this.options.zoomAnimation) { this._backCanvas = this._createCanvas();
this._backCanvas = this._createCanvas();
}
this._ctx = this._canvas.getContext('2d');
this.currentAnimationFrame = -1; this.currentAnimationFrame = -1;
this.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || this.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
@ -51,10 +47,7 @@ L.CanvasLayer = L.Class.extend({
canvas.style.left = 0; canvas.style.left = 0;
canvas.style.pointerEvents = "none"; canvas.style.pointerEvents = "none";
canvas.style.zIndex = this.options.zIndex || 0; canvas.style.zIndex = this.options.zIndex || 0;
var className = 'leaflet-tile-container'; var className = 'leaflet-tile-container leaflet-zoom-animated';
if (this.options.zoomAnimation) {
className += ' leaflet-zoom-animated';
}
canvas.setAttribute('class', className); canvas.setAttribute('class', className);
return canvas; return canvas;
}, },
@ -68,10 +61,8 @@ L.CanvasLayer = L.Class.extend({
var tilePane = this._map._panes.tilePane; var tilePane = this._map._panes.tilePane;
var _container = L.DomUtil.create('div', 'leaflet-layer'); var _container = L.DomUtil.create('div', 'leaflet-layer');
_container.appendChild(this._canvas); _container.appendChild(this._canvas);
if (this.options.zoomAnimation) { _container.appendChild(this._backCanvas);
_container.appendChild(this._backCanvas); this._backCanvas.style.display = 'none';
this._backCanvas.style.display = 'none';
}
tilePane.appendChild(_container); tilePane.appendChild(_container);
this._container = _container; this._container = _container;
@ -87,14 +78,6 @@ L.CanvasLayer = L.Class.extend({
map.on('move', this.render, this); map.on('move', this.render, this);
map.on('resize', this._reset, this); map.on('resize', this._reset, this);
if (this.options.zoomAnimation) {
map.on({
'zoomanim': this._animateZoom,
'zoomend': this._endZoomAnim,
'moveend': this._reset
}, this);
}
if(this.options.tileLoader) { if(this.options.tileLoader) {
this._initTileLoader(); this._initTileLoader();
} }
@ -103,45 +86,40 @@ L.CanvasLayer = L.Class.extend({
}, },
_animateZoom: function (e) { _animateZoom: function (e) {
if (!this._animating) { if (!this._animating) {
this._animating = true; this._animating = true;
} }
var back = this._backCanvas; var back = this._backCanvas;
back.width = this._canvas.width; back.width = this._canvas.width;
back.height = this._canvas.height; back.height = this._canvas.height;
// paint current canvas in back canvas with trasnformation // paint current canvas in back canvas with trasnformation
var pos = this._canvas._leaflet_pos || { x: 0, y: 0 }; var pos = this._canvas._leaflet_pos || { x: 0, y: 0 };
back.getContext('2d').drawImage(this._canvas, 0, 0); back.getContext('2d').drawImage(this._canvas, 0, 0);
L.DomUtil.setPosition(back, L.DomUtil.getPosition(this._canvas)); // hide original
this._canvas.style.display = 'none';
back.style.display = 'block';
var map = this._map;
var scale = map.getZoomScale(e.zoom);
var newCenter = map._latLngToNewLayerPoint(map.getCenter(), e.zoom, e.center);
var oldCenter = map._latLngToNewLayerPoint(e.center, e.zoom, e.center);
// hide original var origin = {
this._canvas.style.display = 'none'; x: newCenter.x - oldCenter.x,
back.style.display = 'block'; y: newCenter.y - oldCenter.y
var map = this._map; };
var scale = map.getZoomScale(e.zoom);
var newCenter = map._latLngToNewLayerPoint(map.getCenter(), e.zoom, e.center);
var oldCenter = map._latLngToNewLayerPoint(e.center, e.zoom, e.center);
var origin = { var bg = back;
x: newCenter.x - oldCenter.x + pos.x, var transform = L.DomUtil.TRANSFORM;
y: newCenter.y - oldCenter.y + pos.y, bg.style[transform] = L.DomUtil.getTranslateString(origin) + ' scale(' + e.scale + ') ';
};
var bg = back;
var transform = L.DomUtil.TRANSFORM;
setTimeout(function() {
bg.style[transform] = L.DomUtil.getTranslateString(origin) + ' scale(' + e.scale + ') ';
}, 0)
}, },
_endZoomAnim: function () { _endZoomAnim: function () {
this._animating = false; this._animating = false;
this._canvas.style.display = 'block'; this._canvas.style.display = 'block';
this._backCanvas.style.display = 'none'; this._backCanvas.style.display = 'none';
this._backCanvas.style[L.DomUtil.TRANSFORM] = '';
}, },
getCanvas: function() { getCanvas: function() {
@ -161,7 +139,6 @@ L.CanvasLayer = L.Class.extend({
map.off({ map.off({
'viewreset': this._reset, 'viewreset': this._reset,
'move': this._render, 'move': this._render,
'moveend': this._reset,
'resize': this._reset, 'resize': this._reset,
'zoomanim': this._animateZoom, 'zoomanim': this._animateZoom,
'zoomend': this._endZoomAnim 'zoomend': this._endZoomAnim
@ -181,9 +158,6 @@ L.CanvasLayer = L.Class.extend({
setZIndex: function(zIndex) { setZIndex: function(zIndex) {
this._canvas.style.zIndex = zIndex; this._canvas.style.zIndex = zIndex;
if (this.options.zoomAnimation) {
this._backCanvas.style.zIndex = zIndex;
}
}, },
bringToFront: function () { bringToFront: function () {
@ -198,12 +172,6 @@ L.CanvasLayer = L.Class.extend({
var size = this._map.getSize(); var size = this._map.getSize();
this._canvas.width = size.x; this._canvas.width = size.x;
this._canvas.height = size.y; this._canvas.height = size.y;
// fix position
var pos = L.DomUtil.getPosition(this._map.getPanes().mapPane);
if (pos) {
L.DomUtil.setPosition(this._canvas, { x: -pos.x, y: -pos.y });
}
this.onResize(); this.onResize();
this._render(); this._render();
}, },

View File

@ -48,8 +48,9 @@ L.TorqueLayer = L.CanvasLayer.extend({
L.CanvasLayer.prototype.initialize.call(this, options); L.CanvasLayer.prototype.initialize.call(this, options);
this.options.renderer = this.options.renderer || 'point'; //this.options.renderer = this.options.renderer || 'point';
this.options.provider = this.options.provider || 'windshaft'; this.options.provider = this.options.provider || 'windshaft';
this.renderer = new WebGLRenderer(this._canvas);
options.ready = function() { options.ready = function() {
self.fire("change:bounds", { self.fire("change:bounds", {
@ -64,7 +65,7 @@ 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);
// for each tile shown on the map request the data // for each tile shown on the map request the data
@ -72,6 +73,28 @@ L.TorqueLayer = L.CanvasLayer.extend({
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;
// prepare vb
var xy = [];
var keys = {}
var init, end;
for(var key = 0; key < tileData.maxDate; ++key) {
init = xy.length/2;
var activePixels = tileData.timeCount[key];
if(activePixels) {
var pixelIndex = tileData.timeIndex[key];
for(var p = 0; p < activePixels; ++p) {
var posIdx = tileData.renderDataPos[pixelIndex + p];
var c = tileData.renderData[pixelIndex + p];
xy.push(tileData.x[posIdx]);
xy.push(255 - tileData.y[posIdx]);
}
}
end = xy.length/2;
keys[key] = [init, end - init + 1];
}
tileData.vb = self.renderer.createVertexBuffer(xy);
tileData.keys = keys;
self._tileLoaded(t, tileData); self._tileLoaded(t, tileData);
if (tileData) { if (tileData) {
self.redraw(); self.redraw();
@ -179,17 +202,55 @@ L.TorqueLayer = L.CanvasLayer.extend({
render: function() { render: function() {
if(this.hidden) return; if(this.hidden) return;
var t, tile, pos; var t, tile, pos;
if (!this.renderer) return;
var gl = this.renderer.gl;
var canvas = this.getCanvas(); var canvas = this.getCanvas();
canvas.width = canvas.width;
var ctx = canvas.getContext('2d');
//var zoom = gl.getUniformLocation(this.renderer.activeProgram(), "zoom");
var tilePos = gl.getUniformLocation(this.renderer.activeProgram(), "tilePos");
var mapSize = gl.getUniformLocation(this.renderer.activeProgram(), "mapSize");
var pSize = gl.getUniformLocation(this.renderer.activeProgram(), "pSize");
//var center = L.CRS.EPSG3857.project(this._map.getCenter());
//var _time = gl.getUniformLocation(this.renderer.activeProgram(), "iGlobalTime");
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.disable(gl.DEPTH_TEST);
//gl.depthFunc(gl.LEQUAL);
gl.clear(gl.COLOR_BUFFER_BIT);
//gl.uniform1f(zoom, this._map.getZoom());
gl.uniform2fv(mapSize, [
this._map.getSize().x,
this._map.getSize().y
]);
var self = this;
function renderKey(tile, key, s) {
if (tile && tile.keys[key]) {
var count = tile.keys[key][1];
if(count) {
var offset = tile.keys[key][0];
pos = self.getTilePos(tile.coord);
gl.uniform1f(pSize, s);
gl.uniform2fv(tilePos, [
pos.x - self._map.getSize().x/2,
-pos.y + self._map.getSize().y/2
]);
setBufferData(gl, self.renderer.activeProgram(), "pos", tile.vb);
gl.drawArrays(gl.POINT, offset, count);
}
}
}
for(t in this._tiles) { for(t in this._tiles) {
tile = this._tiles[t]; tile = this._tiles[t];
if (tile) { renderKey(tile, this.key, 10);
pos = this.getTilePos(tile.coord); renderKey(tile, this.key - 1, 9);
ctx.setTransform(1, 0, 0, 1, pos.x, pos.y); renderKey(tile, this.key - 2, 8);
this.renderer.renderTile(tile, this.key, pos.x, pos.y); renderKey(tile, this.key - 3, 7);
} renderKey(tile, this.key - 4, 6);
renderKey(tile, this.key - 5, 5);
renderKey(tile, this.key - 6, 4);
} }
}, },

View File

@ -0,0 +1,863 @@
/*
** Copyright (c) 2012 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and /or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
// Various functions for helping debug WebGL apps.
WebGLDebugUtils = function() {
/**
* Wrapped logging function.
* @param {string} msg Message to log.
*/
var log = function(msg) {
if (window.console && window.console.log) {
window.console.log(msg);
}
};
/**
* Which arguements are enums.
* @type {!Object.<number, string>}
*/
var glValidEnumContexts = {
// Generic setters and getters
'enable': { 0:true },
'disable': { 0:true },
'getParameter': { 0:true },
// Rendering
'drawArrays': { 0:true },
'drawElements': { 0:true, 2:true },
// Shaders
'createShader': { 0:true },
'getShaderParameter': { 1:true },
'getProgramParameter': { 1:true },
// Vertex attributes
'getVertexAttrib': { 1:true },
'vertexAttribPointer': { 2:true },
// Textures
'bindTexture': { 0:true },
'activeTexture': { 0:true },
'getTexParameter': { 0:true, 1:true },
'texParameterf': { 0:true, 1:true },
'texParameteri': { 0:true, 1:true, 2:true },
'texImage2D': { 0:true, 2:true, 6:true, 7:true },
'texSubImage2D': { 0:true, 6:true, 7:true },
'copyTexImage2D': { 0:true, 2:true },
'copyTexSubImage2D': { 0:true },
'generateMipmap': { 0:true },
// Buffer objects
'bindBuffer': { 0:true },
'bufferData': { 0:true, 2:true },
'bufferSubData': { 0:true },
'getBufferParameter': { 0:true, 1:true },
// Renderbuffers and framebuffers
'pixelStorei': { 0:true, 1:true },
'readPixels': { 4:true, 5:true },
'bindRenderbuffer': { 0:true },
'bindFramebuffer': { 0:true },
'checkFramebufferStatus': { 0:true },
'framebufferRenderbuffer': { 0:true, 1:true, 2:true },
'framebufferTexture2D': { 0:true, 1:true, 2:true },
'getFramebufferAttachmentParameter': { 0:true, 1:true, 2:true },
'getRenderbufferParameter': { 0:true, 1:true },
'renderbufferStorage': { 0:true, 1:true },
// Frame buffer operations (clear, blend, depth test, stencil)
'clear': { 0:true },
'depthFunc': { 0:true },
'blendFunc': { 0:true, 1:true },
'blendFuncSeparate': { 0:true, 1:true, 2:true, 3:true },
'blendEquation': { 0:true },
'blendEquationSeparate': { 0:true, 1:true },
'stencilFunc': { 0:true },
'stencilFuncSeparate': { 0:true, 1:true },
'stencilMaskSeparate': { 0:true },
'stencilOp': { 0:true, 1:true, 2:true },
'stencilOpSeparate': { 0:true, 1:true, 2:true, 3:true },
// Culling
'cullFace': { 0:true },
'frontFace': { 0:true },
};
/**
* Map of numbers to names.
* @type {Object}
*/
var glEnums = null;
/**
* Initializes this module. Safe to call more than once.
* @param {!WebGLRenderingContext} ctx A WebGL context. If
* you have more than one context it doesn't matter which one
* you pass in, it is only used to pull out constants.
*/
function init(ctx) {
if (glEnums == null) {
glEnums = { };
for (var propertyName in ctx) {
if (typeof ctx[propertyName] == 'number') {
glEnums[ctx[propertyName]] = propertyName;
}
}
}
}
/**
* Checks the utils have been initialized.
*/
function checkInit() {
if (glEnums == null) {
throw 'WebGLDebugUtils.init(ctx) not called';
}
}
/**
* Returns true or false if value matches any WebGL enum
* @param {*} value Value to check if it might be an enum.
* @return {boolean} True if value matches one of the WebGL defined enums
*/
function mightBeEnum(value) {
checkInit();
return (glEnums[value] !== undefined);
}
/**
* Gets an string version of an WebGL enum.
*
* Example:
* var str = WebGLDebugUtil.glEnumToString(ctx.getError());
*
* @param {number} value Value to return an enum for
* @return {string} The string version of the enum.
*/
function glEnumToString(value) {
checkInit();
var name = glEnums[value];
return (name !== undefined) ? name :
("*UNKNOWN WebGL ENUM (0x" + value.toString(16) + ")");
}
/**
* Returns the string version of a WebGL argument.
* Attempts to convert enum arguments to strings.
* @param {string} functionName the name of the WebGL function.
* @param {number} argumentIndx the index of the argument.
* @param {*} value The value of the argument.
* @return {string} The value as a string.
*/
function glFunctionArgToString(functionName, argumentIndex, value) {
var funcInfo = glValidEnumContexts[functionName];
if (funcInfo !== undefined) {
if (funcInfo[argumentIndex]) {
return glEnumToString(value);
}
}
if (value === null) {
return "null";
} else if (value === undefined) {
return "undefined";
} else {
return value.toString();
}
}
/**
* Converts the arguments of a WebGL function to a string.
* Attempts to convert enum arguments to strings.
*
* @param {string} functionName the name of the WebGL function.
* @param {number} args The arguments.
* @return {string} The arguments as a string.
*/
function glFunctionArgsToString(functionName, args) {
// apparently we can't do args.join(",");
var argStr = "";
for (var ii = 0; ii < args.length; ++ii) {
argStr += ((ii == 0) ? '' : ', ') +
glFunctionArgToString(functionName, ii, args[ii]);
}
return argStr;
};
function makePropertyWrapper(wrapper, original, propertyName) {
//log("wrap prop: " + propertyName);
wrapper.__defineGetter__(propertyName, function() {
return original[propertyName];
});
// TODO(gmane): this needs to handle properties that take more than
// one value?
wrapper.__defineSetter__(propertyName, function(value) {
//log("set: " + propertyName);
original[propertyName] = value;
});
}
// Makes a function that calls a function on another object.
function makeFunctionWrapper(original, functionName) {
//log("wrap fn: " + functionName);
var f = original[functionName];
return function() {
//log("call: " + functionName);
var result = f.apply(original, arguments);
return result;
};
}
/**
* Given a WebGL context returns a wrapped context that calls
* gl.getError after every command and calls a function if the
* result is not gl.NO_ERROR.
*
* @param {!WebGLRenderingContext} ctx The webgl context to
* wrap.
* @param {!function(err, funcName, args): void} opt_onErrorFunc
* The function to call when gl.getError returns an
* error. If not specified the default function calls
* console.log with a message.
* @param {!function(funcName, args): void} opt_onFunc The
* function to call when each webgl function is called.
* You can use this to log all calls for example.
*/
function makeDebugContext(ctx, opt_onErrorFunc, opt_onFunc) {
init(ctx);
opt_onErrorFunc = opt_onErrorFunc || function(err, functionName, args) {
// apparently we can't do args.join(",");
var argStr = "";
for (var ii = 0; ii < args.length; ++ii) {
argStr += ((ii == 0) ? '' : ', ') +
glFunctionArgToString(functionName, ii, args[ii]);
}
log("WebGL error "+ glEnumToString(err) + " in "+ functionName +
"(" + argStr + ")");
};
// Holds booleans for each GL error so after we get the error ourselves
// we can still return it to the client app.
var glErrorShadow = { };
// Makes a function that calls a WebGL function and then calls getError.
function makeErrorWrapper(ctx, functionName) {
return function() {
if (opt_onFunc) {
opt_onFunc(functionName, arguments);
}
var result = ctx[functionName].apply(ctx, arguments);
var err = ctx.getError();
if (err != 0) {
glErrorShadow[err] = true;
opt_onErrorFunc(err, functionName, arguments);
}
return result;
};
}
// Make a an object that has a copy of every property of the WebGL context
// but wraps all functions.
var wrapper = {};
for (var propertyName in ctx) {
if (typeof ctx[propertyName] == 'function') {
wrapper[propertyName] = makeErrorWrapper(ctx, propertyName);
} else {
makePropertyWrapper(wrapper, ctx, propertyName);
}
}
// Override the getError function with one that returns our saved results.
wrapper.getError = function() {
for (var err in glErrorShadow) {
if (glErrorShadow.hasOwnProperty(err)) {
if (glErrorShadow[err]) {
glErrorShadow[err] = false;
return err;
}
}
}
return ctx.NO_ERROR;
};
return wrapper;
}
function resetToInitialState(ctx) {
var numAttribs = ctx.getParameter(ctx.MAX_VERTEX_ATTRIBS);
var tmp = ctx.createBuffer();
ctx.bindBuffer(ctx.ARRAY_BUFFER, tmp);
for (var ii = 0; ii < numAttribs; ++ii) {
ctx.disableVertexAttribArray(ii);
ctx.vertexAttribPointer(ii, 4, ctx.FLOAT, false, 0, 0);
ctx.vertexAttrib1f(ii, 0);
}
ctx.deleteBuffer(tmp);
var numTextureUnits = ctx.getParameter(ctx.MAX_TEXTURE_IMAGE_UNITS);
for (var ii = 0; ii < numTextureUnits; ++ii) {
ctx.activeTexture(ctx.TEXTURE0 + ii);
ctx.bindTexture(ctx.TEXTURE_CUBE_MAP, null);
ctx.bindTexture(ctx.TEXTURE_2D, null);
}
ctx.activeTexture(ctx.TEXTURE0);
ctx.useProgram(null);
ctx.bindBuffer(ctx.ARRAY_BUFFER, null);
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null);
ctx.bindFramebuffer(ctx.FRAMEBUFFER, null);
ctx.bindRenderbuffer(ctx.RENDERBUFFER, null);
ctx.disable(ctx.BLEND);
ctx.disable(ctx.CULL_FACE);
ctx.disable(ctx.DEPTH_TEST);
ctx.disable(ctx.DITHER);
ctx.disable(ctx.SCISSOR_TEST);
ctx.blendColor(0, 0, 0, 0);
ctx.blendEquation(ctx.FUNC_ADD);
ctx.blendFunc(ctx.ONE, ctx.ZERO);
ctx.clearColor(0, 0, 0, 0);
ctx.clearDepth(1);
ctx.clearStencil(-1);
ctx.colorMask(true, true, true, true);
ctx.cullFace(ctx.BACK);
ctx.depthFunc(ctx.LESS);
ctx.depthMask(true);
ctx.depthRange(0, 1);
ctx.frontFace(ctx.CCW);
ctx.hint(ctx.GENERATE_MIPMAP_HINT, ctx.DONT_CARE);
ctx.lineWidth(1);
ctx.pixelStorei(ctx.PACK_ALIGNMENT, 4);
ctx.pixelStorei(ctx.UNPACK_ALIGNMENT, 4);
ctx.pixelStorei(ctx.UNPACK_FLIP_Y_WEBGL, false);
ctx.pixelStorei(ctx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
// TODO: Delete this IF.
if (ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL) {
ctx.pixelStorei(ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL, ctx.BROWSER_DEFAULT_WEBGL);
}
ctx.polygonOffset(0, 0);
ctx.sampleCoverage(1, false);
ctx.scissor(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.stencilFunc(ctx.ALWAYS, 0, 0xFFFFFFFF);
ctx.stencilMask(0xFFFFFFFF);
ctx.stencilOp(ctx.KEEP, ctx.KEEP, ctx.KEEP);
ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT);
// TODO: This should NOT be needed but Firefox fails with 'hint'
while(ctx.getError());
}
function makeLostContextSimulatingCanvas(canvas) {
var unwrappedContext_;
var wrappedContext_;
var onLost_ = [];
var onRestored_ = [];
var wrappedContext_ = {};
var contextId_ = 1;
var contextLost_ = false;
var resourceId_ = 0;
var resourceDb_ = [];
var numCallsToLoseContext_ = 0;
var numCalls_ = 0;
var canRestore_ = false;
var restoreTimeout_ = 0;
// Holds booleans for each GL error so can simulate errors.
var glErrorShadow_ = { };
canvas.getContext = function(f) {
return function() {
var ctx = f.apply(canvas, arguments);
// Did we get a context and is it a WebGL context?
if (ctx instanceof WebGLRenderingContext) {
if (ctx != unwrappedContext_) {
if (unwrappedContext_) {
throw "got different context"
}
unwrappedContext_ = ctx;
wrappedContext_ = makeLostContextSimulatingContext(unwrappedContext_);
}
return wrappedContext_;
}
return ctx;
}
}(canvas.getContext);
function wrapEvent(listener) {
if (typeof(listener) == "function") {
return listener;
} else {
return function(info) {
listener.handleEvent(info);
}
}
}
var addOnContextLostListener = function(listener) {
onLost_.push(wrapEvent(listener));
};
var addOnContextRestoredListener = function(listener) {
onRestored_.push(wrapEvent(listener));
};
function wrapAddEventListener(canvas) {
var f = canvas.addEventListener;
canvas.addEventListener = function(type, listener, bubble) {
switch (type) {
case 'webglcontextlost':
addOnContextLostListener(listener);
break;
case 'webglcontextrestored':
addOnContextRestoredListener(listener);
break;
default:
f.apply(canvas, arguments);
}
};
}
wrapAddEventListener(canvas);
canvas.loseContext = function() {
if (!contextLost_) {
contextLost_ = true;
numCallsToLoseContext_ = 0;
++contextId_;
while (unwrappedContext_.getError());
clearErrors();
glErrorShadow_[unwrappedContext_.CONTEXT_LOST_WEBGL] = true;
var event = makeWebGLContextEvent("context lost");
var callbacks = onLost_.slice();
setTimeout(function() {
//log("numCallbacks:" + callbacks.length);
for (var ii = 0; ii < callbacks.length; ++ii) {
//log("calling callback:" + ii);
callbacks[ii](event);
}
if (restoreTimeout_ >= 0) {
setTimeout(function() {
canvas.restoreContext();
}, restoreTimeout_);
}
}, 0);
}
};
canvas.restoreContext = function() {
if (contextLost_) {
if (onRestored_.length) {
setTimeout(function() {
if (!canRestore_) {
throw "can not restore. webglcontestlost listener did not call event.preventDefault";
}
freeResources();
resetToInitialState(unwrappedContext_);
contextLost_ = false;
numCalls_ = 0;
canRestore_ = false;
var callbacks = onRestored_.slice();
var event = makeWebGLContextEvent("context restored");
for (var ii = 0; ii < callbacks.length; ++ii) {
callbacks[ii](event);
}
}, 0);
}
}
};
canvas.loseContextInNCalls = function(numCalls) {
if (contextLost_) {
throw "You can not ask a lost contet to be lost";
}
numCallsToLoseContext_ = numCalls_ + numCalls;
};
canvas.getNumCalls = function() {
return numCalls_;
};
canvas.setRestoreTimeout = function(timeout) {
restoreTimeout_ = timeout;
};
function isWebGLObject(obj) {
//return false;
return (obj instanceof WebGLBuffer ||
obj instanceof WebGLFramebuffer ||
obj instanceof WebGLProgram ||
obj instanceof WebGLRenderbuffer ||
obj instanceof WebGLShader ||
obj instanceof WebGLTexture);
}
function checkResources(args) {
for (var ii = 0; ii < args.length; ++ii) {
var arg = args[ii];
if (isWebGLObject(arg)) {
return arg.__webglDebugContextLostId__ == contextId_;
}
}
return true;
}
function clearErrors() {
var k = Object.keys(glErrorShadow_);
for (var ii = 0; ii < k.length; ++ii) {
delete glErrorShadow_[k];
}
}
function loseContextIfTime() {
++numCalls_;
if (!contextLost_) {
if (numCallsToLoseContext_ == numCalls_) {
canvas.loseContext();
}
}
}
// Makes a function that simulates WebGL when out of context.
function makeLostContextFunctionWrapper(ctx, functionName) {
var f = ctx[functionName];
return function() {
// log("calling:" + functionName);
// Only call the functions if the context is not lost.
loseContextIfTime();
if (!contextLost_) {
//if (!checkResources(arguments)) {
// glErrorShadow_[wrappedContext_.INVALID_OPERATION] = true;
// return;
//}
var result = f.apply(ctx, arguments);
return result;
}
};
}
function freeResources() {
for (var ii = 0; ii < resourceDb_.length; ++ii) {
var resource = resourceDb_[ii];
if (resource instanceof WebGLBuffer) {
unwrappedContext_.deleteBuffer(resource);
} else if (resource instanceof WebGLFramebuffer) {
unwrappedContext_.deleteFramebuffer(resource);
} else if (resource instanceof WebGLProgram) {
unwrappedContext_.deleteProgram(resource);
} else if (resource instanceof WebGLRenderbuffer) {
unwrappedContext_.deleteRenderbuffer(resource);
} else if (resource instanceof WebGLShader) {
unwrappedContext_.deleteShader(resource);
} else if (resource instanceof WebGLTexture) {
unwrappedContext_.deleteTexture(resource);
}
}
}
function makeWebGLContextEvent(statusMessage) {
return {
statusMessage: statusMessage,
preventDefault: function() {
canRestore_ = true;
}
};
}
return canvas;
function makeLostContextSimulatingContext(ctx) {
// copy all functions and properties to wrapper
for (var propertyName in ctx) {
if (typeof ctx[propertyName] == 'function') {
wrappedContext_[propertyName] = makeLostContextFunctionWrapper(
ctx, propertyName);
} else {
makePropertyWrapper(wrappedContext_, ctx, propertyName);
}
}
// Wrap a few functions specially.
wrappedContext_.getError = function() {
loseContextIfTime();
if (!contextLost_) {
var err;
while (err = unwrappedContext_.getError()) {
glErrorShadow_[err] = true;
}
}
for (var err in glErrorShadow_) {
if (glErrorShadow_[err]) {
delete glErrorShadow_[err];
return err;
}
}
return wrappedContext_.NO_ERROR;
};
var creationFunctions = [
"createBuffer",
"createFramebuffer",
"createProgram",
"createRenderbuffer",
"createShader",
"createTexture"
];
for (var ii = 0; ii < creationFunctions.length; ++ii) {
var functionName = creationFunctions[ii];
wrappedContext_[functionName] = function(f) {
return function() {
loseContextIfTime();
if (contextLost_) {
return null;
}
var obj = f.apply(ctx, arguments);
obj.__webglDebugContextLostId__ = contextId_;
resourceDb_.push(obj);
return obj;
};
}(ctx[functionName]);
}
var functionsThatShouldReturnNull = [
"getActiveAttrib",
"getActiveUniform",
"getBufferParameter",
"getContextAttributes",
"getAttachedShaders",
"getFramebufferAttachmentParameter",
"getParameter",
"getProgramParameter",
"getProgramInfoLog",
"getRenderbufferParameter",
"getShaderParameter",
"getShaderInfoLog",
"getShaderSource",
"getTexParameter",
"getUniform",
"getUniformLocation",
"getVertexAttrib"
];
for (var ii = 0; ii < functionsThatShouldReturnNull.length; ++ii) {
var functionName = functionsThatShouldReturnNull[ii];
wrappedContext_[functionName] = function(f) {
return function() {
loseContextIfTime();
if (contextLost_) {
return null;
}
return f.apply(ctx, arguments);
}
}(wrappedContext_[functionName]);
}
var isFunctions = [
"isBuffer",
"isEnabled",
"isFramebuffer",
"isProgram",
"isRenderbuffer",
"isShader",
"isTexture"
];
for (var ii = 0; ii < isFunctions.length; ++ii) {
var functionName = isFunctions[ii];
wrappedContext_[functionName] = function(f) {
return function() {
loseContextIfTime();
if (contextLost_) {
return false;
}
return f.apply(ctx, arguments);
}
}(wrappedContext_[functionName]);
}
wrappedContext_.checkFramebufferStatus = function(f) {
return function() {
loseContextIfTime();
if (contextLost_) {
return wrappedContext_.FRAMEBUFFER_UNSUPPORTED;
}
return f.apply(ctx, arguments);
};
}(wrappedContext_.checkFramebufferStatus);
wrappedContext_.getAttribLocation = function(f) {
return function() {
loseContextIfTime();
if (contextLost_) {
return -1;
}
return f.apply(ctx, arguments);
};
}(wrappedContext_.getAttribLocation);
wrappedContext_.getVertexAttribOffset = function(f) {
return function() {
loseContextIfTime();
if (contextLost_) {
return 0;
}
return f.apply(ctx, arguments);
};
}(wrappedContext_.getVertexAttribOffset);
wrappedContext_.isContextLost = function() {
return contextLost_;
};
return wrappedContext_;
}
}
return {
/**
* Initializes this module. Safe to call more than once.
* @param {!WebGLRenderingContext} ctx A WebGL context. If
}
* you have more than one context it doesn't matter which one
* you pass in, it is only used to pull out constants.
*/
'init': init,
/**
* Returns true or false if value matches any WebGL enum
* @param {*} value Value to check if it might be an enum.
* @return {boolean} True if value matches one of the WebGL defined enums
*/
'mightBeEnum': mightBeEnum,
/**
* Gets an string version of an WebGL enum.
*
* Example:
* WebGLDebugUtil.init(ctx);
* var str = WebGLDebugUtil.glEnumToString(ctx.getError());
*
* @param {number} value Value to return an enum for
* @return {string} The string version of the enum.
*/
'glEnumToString': glEnumToString,
/**
* Converts the argument of a WebGL function to a string.
* Attempts to convert enum arguments to strings.
*
* Example:
* WebGLDebugUtil.init(ctx);
* var str = WebGLDebugUtil.glFunctionArgToString('bindTexture', 0, gl.TEXTURE_2D);
*
* would return 'TEXTURE_2D'
*
* @param {string} functionName the name of the WebGL function.
* @param {number} argumentIndx the index of the argument.
* @param {*} value The value of the argument.
* @return {string} The value as a string.
*/
'glFunctionArgToString': glFunctionArgToString,
/**
* Converts the arguments of a WebGL function to a string.
* Attempts to convert enum arguments to strings.
*
* @param {string} functionName the name of the WebGL function.
* @param {number} args The arguments.
* @return {string} The arguments as a string.
*/
'glFunctionArgsToString': glFunctionArgsToString,
/**
* Given a WebGL context returns a wrapped context that calls
* gl.getError after every command and calls a function if the
* result is not NO_ERROR.
*
* You can supply your own function if you want. For example, if you'd like
* an exception thrown on any GL error you could do this
*
* function throwOnGLError(err, funcName, args) {
* throw WebGLDebugUtils.glEnumToString(err) +
* " was caused by call to " + funcName;
* };
*
* ctx = WebGLDebugUtils.makeDebugContext(
* canvas.getContext("webgl"), throwOnGLError);
*
* @param {!WebGLRenderingContext} ctx The webgl context to wrap.
* @param {!function(err, funcName, args): void} opt_onErrorFunc The function
* to call when gl.getError returns an error. If not specified the default
* function calls console.log with a message.
* @param {!function(funcName, args): void} opt_onFunc The
* function to call when each webgl function is called. You
* can use this to log all calls for example.
*/
'makeDebugContext': makeDebugContext,
/**
* Given a canvas element returns a wrapped canvas element that will
* simulate lost context. The canvas returned adds the following functions.
*
* loseContext:
* simulates a lost context event.
*
* restoreContext:
* simulates the context being restored.
*
* lostContextInNCalls:
* loses the context after N gl calls.
*
* getNumCalls:
* tells you how many gl calls there have been so far.
*
* setRestoreTimeout:
* sets the number of milliseconds until the context is restored
* after it has been lost. Defaults to 0. Pass -1 to prevent
* automatic restoring.
*
* @param {!Canvas} canvas The canvas element to wrap.
*/
'makeLostContextSimulatingCanvas': makeLostContextSimulatingCanvas,
/**
* Resets a context to the initial state.
* @param {!WebGLRenderingContext} ctx The webgl context to
* reset.
*/
'resetToInitialState': resetToInitialState
};
}();

218
lib/torque/leaflet/webgl.js Normal file
View File

@ -0,0 +1,218 @@
function glGetError(gl) {
var ctx, error;
ctx = gl.getCurrentContext();
error = ctx.errorValue;
ctx.errorValue = GL_NO_ERROR;
return error;
}
// webgl utils
function shaderProgram(gl, vs, fs) {
var prog = gl.createProgram();
var addshader = function(type, source) {
var s = gl.createShader((type == 'vertex') ?
gl.VERTEX_SHADER : gl.FRAGMENT_SHADER);
gl.shaderSource(s, source);
gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
throw "Could not compile "+type+
" shader:\n\n"+gl.getShaderInfoLog(s);
}
gl.attachShader(prog, s);
};
addshader('vertex', vs);
addshader('fragment', fs);
gl.linkProgram(prog);
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
throw "Could not link the shader program!";
}
return prog;
}
function createVertexBuffer(gl, rsize, arr) {
var buff = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buff);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(arr), gl.STATIC_DRAW);
buff.vSize = rsize;
return buff;
}
function setBufferData(gl, prog, attr_name, buff) {
gl.bindBuffer(gl.ARRAY_BUFFER, buff);
var attr = gl.getAttribLocation(prog, attr_name);
gl.enableVertexAttribArray(attr);
gl.vertexAttribPointer(attr, buff.vSize, gl.FLOAT, false, 0, 0);
}
function initFB() {
var fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
fb.width = 512;
fb.height = 512;
}
function WebGLRenderer(el) {
this.gl = el.getContext('webgl');
this._init();
this.width = el.width;
this.height = el.height;
this.image_cache = {}
}
/*
* #mn_mappluto_13v1{
line-color: #FFF;
line-width: 1;
line-opacity: 1;
[numfloors<=104]{building-height:52;}[numfloors<=100]{building-height:50;}[numfloors<=96]{building-height:48;}[numfloors<=92]{building-height:46;}[numfloors<=88]{building-height:44;}[numfloors<=84]{building-height:42;}[numfloors<=80]{building-height:40;}[numfloors<=76]{building-height:38;}[numfloors<=72]{building-height:36;}[numfloors<=68]{building-height:34;}[numfloors<=64]{building-height:32;}[numfloors<=60]{building-height:30;}[numfloors<=56]{building-height:28;}[numfloors<=52]{building-height:26;}[numfloors<=48]{building-height:24;}[numfloors<=44]{building-height:22;}[numfloors<=40]{building-height:20;}[numfloors<=36]{building-height:18;}[numfloors<=32]{building-height:16;}[numfloors<=28]{building-height:14;}[numfloors<=24]{building-height:12;}[numfloors<=20]{building-height:10;}[numfloors<=16]{building-height:8;}[numfloors<=12]{building-height:0;}
}
#mn_mappluto_13v1 [ spiderman <= 576] {
building-fill: #B10026;
}
#mn_mappluto_13v1 [ spiderman <= 168] {
building-fill: #E31A1C;
}
#mn_mappluto_13v1 [ spiderman <= 98] {
building-fill: #FC4E2A;
}
#mn_mappluto_13v1 [ spiderman <= 73] {
building-fill: #FD8D3C;
}
#mn_mappluto_13v1 [ spiderman <= 45] {
building-fill: #FEB24C;
}
#mn_mappluto_13v1 [ spiderman <= 29] {
building-fill: #FED976;
}
#mn_mappluto_13v1 [ spiderman <= 14] {
building-fill: #FFFFB2;
}
*/
//256/EARTH_RADIUS * 2 * Math.PI
//6378137
WebGLRenderer.prototype._init = function() {
var gl = this.gl;
var prog = shaderProgram(gl,
"precision highp float;\n"+
//"#define M_PI 3.1415926535897932384626433832795\n" +
//"uniform float zoom;" +
"uniform vec2 tilePos;" +
"uniform vec2 mapSize;" +
"uniform float pSize;" +
"attribute vec2 pos;"+
"void main() {"+
//" float er = 6378137.0;" +
//" float s = (256.0*pow(2.0, zoom))/(er*M_PI*2.0);"+
" gl_PointSize = pSize;" +
" vec2 p = vec2(pos.x, -pos.y);" +
" gl_Position = vec4((2.0*(p + tilePos)/mapSize), 0.0, 1.0);"+
//" gl_Position = vec4((2.0*pos/mapSize) - vec2(1.0, 1.0), 0.0, 1.0);"+
//" gl_Position = vec4(0.0, 0.0, 0.0, 1.0);"+
"}",
"precision highp float;"+
"void main() {"+
"float d = 1.0 - pow(length(2.0*vec2(gl_PointCoord.s - 0.5, gl_PointCoord.t - 0.5)), 5.0);" +
"gl_FragColor = vec4(d, 0.0, 0.0, d);" +
"}"
);
this.program = prog;
gl.useProgram(prog);
/*this.vertexBuffer = createVertexBuffer(gl, 2, [
-1, -1,
-1, 1,
1, -1,
1, 1
]);
setBufferData(gl, prog, "pos", this.vertexBuffer);
*/
var err = gl.errorValue;
if(err !== 0) {
console.log(err);
}
};
function uploadTexture(gl, img) {
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.bindTexture(gl.TEXTURE_2D, null);
return texture;
}
WebGLRenderer.prototype.activeProgram = function() {
return this.program;
}
WebGLRenderer.prototype.createVertexBuffer = function(data) {
return createVertexBuffer(this.gl, 2, data);
}
/*
WebGLRenderer.prototype.loadImageTile = function(tile) {
var self = this;
//var layer = 'http://b.tiles.mapbox.com/v3/mapbox.mapbox-light/{{z}}/{{x}}/{{y}}.png64';
layer = 'http://tile.stamen.com/toner/{{z}}/{{x}}/{{y}}.png';
var url = layer.replace('{{z}}', tile.zoom).replace('{{x}}', tile.i).replace('{{y}}', tile.j);
var k = tile.zoom + '-' + tile.i + '-' + tile.j;
var i = this.image_cache[k];
if(i === undefined) {
self.image_cache[k] = null;
var img = new Image();
img.crossOrigin = "*";
img.onload = function() {
self.image_cache[k] = uploadTexture(self.gl, img);
console.log(k + " loaded");
//requestAnimationFrame(self.render);
};
img.src = url;
}
}
WebGLRenderer.prototype.renderTiles = function(tiles, center, zoom) {
var gl = this.gl;
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
var mapSize = gl.getUniformLocation(this.program, "mapSize");
gl.uniform2fv(mapSize, [this.width, this.height]);
var zoom = gl.getUniformLocation(this.program, "zoom");
gl.uniform1f(zoom, zoom);
var mapPos = gl.getUniformLocation(this.program, "mapPos");
gl.uniform2fv(mapPos, [center.x, center.y]);
var tileImage = gl.getUniformLocation(this.program, "tileImage");
for(var i = 0; i < tiles.length; ++i) {
var tile = tiles[i];
this.loadImageTile(tile);
var k = tile.zoom + '-' + tile.i + '-' + tile.j;
var img = this.image_cache[k];
if(img) {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, img);
gl.uniform1i(tileImage, 0);
var tilePos = gl.getUniformLocation(this.program, "tilePos");
gl.uniform2fv(tilePos, [tile.x, tile.y]);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
}
var err = gl.errorValue;
if(err !== 0) {
console.log(err);
}
};
*/

View File

@ -44,6 +44,7 @@
json.prototype = { json.prototype = {
/** /**
* return the torque tile encoded in an efficient javascript * return the torque tile encoded in an efficient javascript
* structure: * structure:

View File

@ -40,6 +40,41 @@
}; };
json.prototype = { json.prototype = {
aggregateByKey: function(callback) {
var url = this.templateUrl
.replace('{x}', 0)
.replace('{y}', 0)
.replace('{z}', 0)
.replace('{s}', 0)
var self = this;
var extra = this._extraParams();
torque.net.get( url, function (data) {
var rows = JSON.parse(data.responseText);
callback(self._aggregateByKey(rows));
})
},
_aggregateByKey: function(rows) {
function getKeys(row) {
var keys = {};
var dates = row.dates__uint16;
var vals = row.vals__uint8;
var valuesCount = vals.length;
for (var s = 0; s < valuesCount; ++s) {
keys[dates[s]] = vals[s];
}
return keys;
}
var keys = {};
for (r = 0; r < rows.length; ++r) {
var rowKeys = getKeys(rows[r]);
for(var k in rowKeys) {
keys[k] = keys[k] || 0;
keys[k] += rowKeys[k];
}
}
return keys;
},
/** /**
* return the torque tile encoded in an efficient javascript * return the torque tile encoded in an efficient javascript