A thing made with Squares, WebGL demo.
Last active
April 28, 2025 09:23
-
-
Save migurski/5130639 to your computer and use it in GitHub Desktop.
GL-Solar, Rainbow Road edition
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// features_list() is used via a Web Worker, called from Map class in map.js. | |
// | |
self.addEventListener('message', onmessage); | |
var pi = Math.PI; | |
var rpm = 1/6378137; | |
function onmessage(e) | |
{ | |
var start = (new Date()).getTime(); | |
var node_id = e.data.node_id, | |
features = e.data.features, | |
zoom = e.data.zoom, | |
list = features_list(features, zoom); | |
var end = (new Date()).getTime(); | |
self.postMessage({node_id: node_id, list: list, elapsed: end - start}); | |
} | |
function features_list(features, zoom) | |
{ | |
var reds = {motorway: 1, motorway_link: 1, trunk: 1, trunk_link: 1, primary: 1}; | |
var pixel = 2 * pi / (1 << (zoom + 8)), | |
floats = []; | |
for(var i in features) | |
{ | |
var props = features[i]['properties'], | |
geometry = features[i]['geometry'], | |
parts = (geometry['type'] == 'LineString') ? [geometry['coordinates']] : geometry['coordinates']; | |
if(zoom < 14 && props['kind'] != 'major_road' && props['kind'] != 'highway') | |
{ | |
continue; | |
} | |
var widths = highway_widths(props['highway'], props['kind'], zoom), | |
inner = widths[0], | |
outer = widths[1], | |
incap = inner/7, | |
outcap = inner/20; | |
var layer = highway_layer(props['highway'], props['explicit_layer'], props['is_bridge'], props['is_tunnel']); | |
for(var j in parts) | |
{ | |
for(var k = parts[j].length - 2; k >= 0; k--) | |
{ | |
// Positions of line segment start and end in mercator | |
var loc1 = {lon: parts[j][k][0], lat: parts[j][k][1]}, | |
loc2 = {lon: parts[j][k+1][0], lat: parts[j][k+1][1]}, | |
pA = project(loc1), | |
pZ = project(loc2), | |
len = hypot(pA, pZ); | |
// replace end point | |
parts[j].splice(k+1, 1, pZ); | |
var pieces = Math.floor(len / (10 * pixel)), | |
length = len / pieces; | |
for(var l = 1; l < pieces; l++) | |
{ | |
var p = lerp(pA, pZ, 1 - l/pieces); | |
// splice in new point | |
parts[j].splice(k+1, 0, p); | |
} | |
} | |
// use the final pA from above | |
parts[j][0] = pA; | |
for(var k = 0; k < parts[j].length - 1; k++) | |
{ | |
// Positions of line segment start and end in mercator | |
var p1 = parts[j][k], | |
p2 = parts[j][k+1]; | |
// Offsets to the front, back and sides of line segment in mercator | |
var θ = Math.atan2(p2.y - p1.y, p2.x - p1.x), | |
ux = Math.cos(θ), | |
uy = Math.sin(θ), | |
vx = Math.cos(θ + pi/2), | |
vy = Math.sin(θ + pi/2); | |
// Positions of outer corners of line segment capped line in mercator | |
var pa = {x: p1.x - vx*inner - ux*incap, y: p1.y - vy*inner - uy*incap}, | |
pb = {x: p2.x - vx*inner + ux*incap, y: p2.y - vy*inner + uy*incap}, | |
pc = {x: p2.x + vx*inner + ux*incap, y: p2.y + vy*inner + uy*incap}, | |
pd = {x: p1.x + vx*inner - ux*incap, y: p1.y + vy*inner - uy*incap}, | |
z = layer; | |
// Render colors, including alpha value based on is_tunnel | |
var r = (props['highway'] in reds) ? 211/255 : 147/255, | |
g = (props['highway'] in reds) ? 54/255 : 161/255, | |
b = (props['highway'] in reds) ? 130/255 : 161/255, | |
a = (props['is_tunnel'] == 'yes') ? .4 : 1; | |
// Two triangles covering this line segment, with (x, y, z, r, g, b, a) values. | |
floats = floats.concat([pa.x, pa.y, z, 1, 0, 0, a, k, pb.x, pb.y, z, 1,.5, 0, a, k, pc.x, pc.y, z, 1, 1, 0, a, k]); | |
floats = floats.concat([pa.x, pa.y, z, 1, 0, 0, a, k, pc.x, pc.y, z, 1, 1, 0, a, k, pd.x, pd.y, z, 1,.5, 0, a, k]); | |
// Two additional triangles for bridge casings. | |
if(zoom >= 15 && props['is_bridge'] == 'yes') | |
{ | |
// Positions of outer corners of line segment capped line in mercator | |
var pa = {x: p1.x - vx*outer - ux*outcap, y: p1.y - vy*outer - uy*outcap}, | |
pb = {x: p2.x - vx*outer + ux*outcap, y: p2.y - vy*outer + uy*outcap}, | |
pc = {x: p2.x + vx*outer + ux*outcap, y: p2.y + vy*outer + uy*outcap}, | |
pd = {x: p1.x + vx*outer - ux*outcap, y: p1.y + vy*outer - uy*outcap}, | |
z = layer - 10; | |
// Render colors for map background and adjusted z-index. | |
var r = 0 /* 253/255 */, | |
g = 0 /* 246/255 */, | |
b = 0 /* 227/255 */; | |
floats = floats.concat([pa.x, pa.y, z, r, g, b, a, -1, pb.x, pb.y, z, r, g, b, a, -1, pc.x, pc.y, z, r, g, b, a, -1]); | |
floats = floats.concat([pa.x, pa.y, z, r, g, b, a, -1, pc.x, pc.y, z, r, g, b, a, -1, pd.x, pd.y, z, r, g, b, a, -1]); | |
} | |
} | |
} | |
} | |
return floats; | |
} | |
function project(loc) | |
{ | |
var λ = pi * loc.lon / 180, | |
φ = pi * loc.lat / 180; | |
var x = λ, | |
y = Math.log(Math.tan(pi/4 + φ/2)); | |
return {x: x, y: y}; | |
} | |
function hypot(pA, pB) | |
{ | |
return Math.sqrt(Math.pow(pA.x - pB.x, 2) + Math.pow(pA.y - pB.y, 2)); | |
} | |
function lerp(pA, pB, amt) | |
{ | |
return {x: pA.x * (1-amt) + pB.x * amt, y: pA.y * (1-amt) + pB.y * amt}; | |
} | |
// | |
// Larger numbers cause roads to shrink faster on zoom out. | |
// | |
var highway_coefficients = { | |
motorway: .6, trunk: .6, primary: .6, secondary: .6, tertiary: .6, | |
motorway_link: .7, trunk_link: .7, primary_link: .7, secondary_link: .7, tertiary_link: .7 | |
}; | |
// | |
// Get highway width in mercator radians. | |
// | |
function highway_widths(highway, kind, zoom) | |
{ | |
var pixel = 2 * pi / (1 << (zoom + 8)), | |
coeff = (highway in highway_coefficients) ? highway_coefficients[highway] : .8, | |
coeff = (kind == 'path' ? .9 : coeff), | |
scale = Math.pow(2, coeff * (zoom - 18)); | |
if(highway == 'motorway') { | |
var inner = 14; | |
} else if(kind == 'path' || kind == 'rail' || highway == 'service') { | |
var inner = 3; | |
} else { | |
var inner = 6.5; | |
} | |
return [inner * pixel * scale, (inner + 4) * pixel * scale]; | |
} | |
// | |
// Smaller numbers prioritize roads in front of other roads. | |
// | |
var highway_priorities = { | |
motorway: 0, trunk: 1, primary: 2, secondary: 3, tertiary: 4, | |
motorway_link: 5, trunk_link: 5, primary_link: 5, secondary_link: 5, tertiary_link: 5, | |
residential: 6, unclassified: 6, road: 6, | |
unclassified: 7, service: 7, minor: 7 | |
}; | |
// | |
// Get highway layer (z-index) as an integer. | |
// | |
function highway_layer(highway, explicit_layer, is_bridge, is_tunnel) | |
{ | |
// explicit layering mostly wins | |
var layer = (explicit_layer == undefined) ? 0 : explicit_layer * 1000; | |
// implicit layering less important. | |
if(is_bridge == 'yes') | |
{ | |
layer += 100; | |
} | |
if(is_tunnel == 'yes') | |
{ | |
layer -= 100; | |
} | |
// leave the +/-10 order of magnitude open for bridge casings. | |
// adjust slightly based on priority derived from highway type | |
layer -= (highway in highway_priorities) ? highway_priorities[highway] : 9; | |
return layer; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function linkProgram(gl, vsource, fsource) | |
{ | |
if(gl == undefined) | |
{ | |
alert("Your browser does not support WebGL, try Google Chrome? Sorry."); | |
throw "Your browser does not support WebGL, try Google Chrome? Sorry."; | |
} | |
var program = gl.createProgram(), | |
vshader = createShader(gl, vsource, gl.VERTEX_SHADER), | |
fshader = createShader(gl, fsource, gl.FRAGMENT_SHADER); | |
gl.attachShader(program, vshader); | |
gl.attachShader(program, fshader); | |
gl.linkProgram(program); | |
if(!gl.getProgramParameter(program, gl.LINK_STATUS)) | |
{ | |
throw gl.getProgramInfoLog(program); | |
} | |
return program; | |
} | |
function createShader(gl, source, type) | |
{ | |
var shader = gl.createShader(type); | |
gl.shaderSource(shader, source); | |
gl.compileShader(shader); | |
if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) | |
{ | |
throw gl.getShaderInfoLog(shader); | |
} | |
return shader; | |
} | |
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ | |
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating | |
// requestAnimationFrame polyfill by Erik Möller | |
// fixes from Paul Irish and Tino Zijdel | |
(function() { | |
var lastTime = 0; | |
var vendors = ['ms', 'moz', 'webkit', 'o']; | |
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { | |
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; | |
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] | |
|| window[vendors[x]+'CancelRequestAnimationFrame']; | |
} | |
if (!window.requestAnimationFrame) | |
window.requestAnimationFrame = function(callback, element) { | |
var currTime = new Date().getTime(); | |
var timeToCall = Math.max(0, 16 - (currTime - lastTime)); | |
var id = window.setTimeout(function() { callback(currTime + timeToCall); }, | |
timeToCall); | |
lastTime = currTime + timeToCall; | |
return id; | |
}; | |
if (!window.cancelAnimationFrame) | |
window.cancelAnimationFrame = function(id) { | |
clearTimeout(id); | |
}; | |
}()); | |
function endianness() | |
{ | |
if(window.ArrayBuffer == undefined) | |
{ | |
alert("Your browser does not support ArrayBuffer, try Google Chrome? Sorry."); | |
throw "Your browser does not support ArrayBuffer, try Google Chrome? Sorry."; | |
} | |
var b = new ArrayBuffer(4), | |
f = new Float32Array(b), | |
u = new Uint32Array(b); | |
f[0] = 1.0; | |
if(u[0] == 32831) { | |
return 'big'; | |
} else { | |
return 'little'; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>GL-Solar (Squares demo)</title> | |
<script src="http://teczno.com/squares/Squares-D3-0.0.5.min.js" type="application/javascript"></script> | |
<script src="gl-boilerplate.js" type="application/javascript"></script> | |
<script src="tile-queue.js" type="application/javascript"></script> | |
<script src="map.js" type="application/javascript"></script> | |
<link rel="stylesheet" href="http://www.openstreetmap.us/~migurski/style.css"> | |
<link rel="stylesheet" href="style.css"> | |
</head> | |
<body> | |
<div id="map"></div> | |
<p> | |
<a href="http://mike.teczno.com">Michal Migurski</a>, Feb 2013. | |
</p> | |
<script id="shader-vertex" type="x-shader/x-vertex"> | |
const float pi = 3.14159265359; | |
const mat4 view = mat4 (2.0/{CANVAS_WIDTH}, 0, 0, 0, 0, -2.0/{CANVAS_HEIGHT}, 0, 0, 0, 0, -0.0001, 0, -1, 1, 0, 1); | |
uniform mat4 panzoom; | |
uniform float frame; | |
attribute vec3 xyz; | |
attribute vec4 rgba; | |
attribute float seg; | |
varying float bounce; | |
varying vec4 color; | |
varying mat4 bloop; | |
varying vec4 pos; | |
void main() | |
{ | |
pos = view * panzoom * vec4(xyz, 1); | |
bounce = log(xyz.z + 1.) * atan(frame/5000.) * .002; | |
bloop = mat4(1, 0, 0, 0, | |
0, 1, 0, 0, | |
0, 0, 1, 0, | |
sin(pos.x * 5.0) * sin(frame / 10.) * bounce, | |
sin(pos.y * 9.6) * cos(frame / 10.) * bounce, | |
0, 1); | |
gl_Position = bloop * pos; | |
if(seg >= 0.) { | |
color = vec4(.5 * cos(frame/4. - seg/2. + 0. * pi/3.) + .5, | |
.5 * cos(frame/4. - seg/2. + 2. * pi/3.) + .5, | |
.5 * cos(frame/4. - seg/2. + 4. * pi/3.) + .5, | |
rgba.a); | |
} else { | |
color = rgba; | |
} | |
} | |
</script> | |
<script id="shader-fragment" type="x-shader/x-fragment"> | |
precision mediump float; | |
const vec3 bg = vec3(0, 0, 0); // vec3(0.992, 0.965, 0.890); | |
varying vec4 color; | |
void main() | |
{ | |
// instead of using the full RGBA, do a linear mix with background color | |
gl_FragColor = vec4(mix(bg.rgb, color.rgb, color.a), 1); | |
} | |
</script> | |
<script type="application/javascript"> | |
<!-- | |
var ctx = get_webgl_context(); | |
var geo = new sq.Geo.Mercator(); | |
var map = new Map(document.getElementById('map'), geo, {lat: 37.73570, lon: -122.40806}, 16.5); | |
// | |
// Return a pair of functions to call from Map class in map.js, one to | |
// push new data into xyz-rgba buffer and the other to trigger redraw. | |
// | |
function get_webgl_context(matrix) | |
{ | |
var map = document.getElementById('map'), | |
c = document.createElement('canvas'); | |
c.width = map.clientWidth; | |
c.height = map.clientHeight; | |
c.style.position = 'absolute'; | |
map.insertBefore(c, null); | |
var gl = c.getContext('experimental-webgl'), | |
vsource = document.getElementById('shader-vertex').text, | |
vsource = vsource.replace('{CANVAS_WIDTH}', c.width.toFixed(1)), | |
vsource = vsource.replace('{CANVAS_HEIGHT}', c.height.toFixed(1)), | |
fsource = document.getElementById('shader-fragment').text, | |
program = linkProgram(gl, vsource, fsource); | |
gl.useProgram(program); | |
var xyzrgba_buffer = gl.createBuffer(), | |
xyz_attrib = gl.getAttribLocation(program, 'xyz'), | |
rgba_attrib = gl.getAttribLocation(program, 'rgba'), | |
seg_attrib = gl.getAttribLocation(program, 'seg'), | |
panzoom = gl.getUniformLocation(program, 'panzoom'), | |
frameuni = gl.getUniformLocation(program, 'frame'), | |
length = 0; | |
gl.enableVertexAttribArray(xyz_attrib); | |
gl.enableVertexAttribArray(rgba_attrib); | |
gl.enableVertexAttribArray(seg_attrib); | |
gl.bindBuffer(gl.ARRAY_BUFFER, xyzrgba_buffer); | |
function data(xys) | |
{ | |
gl.bufferData(gl.ARRAY_BUFFER, xys, gl.DYNAMIC_DRAW); | |
length = xys.length/8; | |
} | |
function draw(size, ul, lr) | |
{ | |
// mx+b style transformation. | |
var mx = size.x / (lr.x - ul.x), bx = -mx * ul.x, | |
my = size.y / (lr.y - ul.y), by = -my * ul.y; | |
var matrix = new Float32Array([mx, 0, 0, 0, 0, my, 0, 0, 0, 0, 1, 0, bx, by, 0, 1]); | |
gl.uniformMatrix4fv(panzoom, false, matrix); | |
} | |
var frame = 1; | |
paint(); | |
function paint() | |
{ | |
gl.clearColor(0, 0, 0, 1); // gl.clearColor(253/255, 246/255, 227/255, 1); | |
gl.clear(gl.COLOR_BUFFER_BIT); | |
gl.enable(gl.DEPTH_TEST); | |
gl.uniform1f(frameuni, frame); | |
gl.vertexAttribPointer(xyz_attrib, 3, gl.FLOAT, false, 4*8, 0); | |
gl.vertexAttribPointer(rgba_attrib, 4, gl.FLOAT, false, 4*8, 4*3); | |
gl.vertexAttribPointer(seg_attrib, 1, gl.FLOAT, false, 4*8, 4*7); | |
gl.drawArrays(gl.TRIANGLES, 0, length); | |
/* | |
gl.drawArrays(gl.TRIANGLES, 0, Math.min(length/2)); | |
gl.drawArrays(gl.TRIANGLES, Math.min(length/2), length - Math.min(length/2)); | |
*/ | |
frame += 1; | |
requestAnimationFrame(paint); | |
} | |
return {draw: draw, data: data}; | |
} | |
//--> | |
</script> | |
<iframe width="40" height="30" src="http://www.youtube.com/embed/eJF-u9xWIH8?rel=0&autoplay=1" frameborder="0" allowfullscreen></iframe> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Sample Map class for use with Squares. | |
// Implements the Map interface from v0.0.5: | |
// https://github.com/migurski/Squares/blob/315e37bc/src/Base.ts | |
// | |
// Renders data with WebGL, and relies on Web Worker in feature-arrayer.js | |
// and global ctx variable from get_webgl_context() in index.html. | |
// | |
function Map(parent, proj, loc, zoom) | |
{ | |
this.queue = new Queue(); | |
this.timeout = false; | |
this.workers = [new Worker('feature-arrayer.js'), new Worker('feature-arrayer.js')]; | |
this.selection = d3.select(parent); | |
this.parent = parent; | |
var size = sq.Mouse.element_size(this.parent), coord = proj.locationCoordinate(loc).zoomTo(zoom); | |
this.grid = new sq.Grid.Grid(size.x, size.y, coord, 0); | |
this.projection = proj; | |
sq.Mouse.link_control(this.selection, new sq.Mouse.Control(this, false)); | |
sq.Hash.link_control(this); | |
var map = this; | |
d3.select(window).on('resize.map', function() { map.update_gridsize() }); | |
this.workers[0].addEventListener('message', function(e) { map.new_data(e.data.node_id, e.data.list, e.data.elapsed) }, false); | |
this.workers[1].addEventListener('message', function(e) { map.new_data(e.data.node_id, e.data.list, e.data.elapsed) }, false); | |
this.selection.selectAll('div.tile').remove(); | |
this.redraw(false); | |
} | |
Map.prototype = { | |
update_gridsize: function() | |
{ | |
var size = sq.Mouse.element_size(this.parent); | |
this.grid.resize(size.x, size.y); | |
}, | |
pointLocation: function(point) | |
{ | |
var coord = this.grid.pointCoordinate(point ? point : this.grid.center); | |
return this.projection.coordinateLocation(coord); | |
}, | |
locationPoint: function(loc) | |
{ | |
var coord = this.projection.locationCoordinate(loc); | |
return this.grid.coordinatePoint(coord); | |
}, | |
setCenterZoom: function(loc, zoom) | |
{ | |
this.grid.setCenter(this.projection.locationCoordinate(loc, zoom)); | |
this.redraw(true); | |
}, | |
onMoved: function(callback) | |
{ | |
this.moved_callback = callback; | |
}, | |
redraw: function(moved) | |
{ | |
var tiles = this.grid.visibleTiles(), | |
join = this.selection.selectAll('div.tile').data(tiles, tile_key); | |
var map = this; | |
join.exit() | |
.remove() | |
.each(function(tile, i) { map.exit_handler(tile, this) }); | |
join.enter() | |
.append('div') | |
.attr('class', 'tile') | |
.style('position', 'absolute') | |
.style('margin', '0') | |
.style('padding', '0') | |
.style('border', '0') | |
.style('-webkit-transform-origin', '0px 0px') | |
.each(function(tile, i) { map.enter_handler(tile, this) }); | |
this.selection.selectAll('div.tile') | |
.style('left', tile_left) | |
.style('top', tile_top) | |
.style('width', tile_width) | |
.style('height', tile_height); | |
if(this.moved_callback) | |
{ | |
this.moved_callback(this); | |
} | |
this.queue.process(); | |
this.render(); | |
}, | |
update: function() | |
{ | |
var len = 0, | |
offs = []; | |
// get the total length of all arrays | |
this.selection.selectAll('div.tile') | |
.each(function() { if(this.array) { len += this.array.length } }); | |
var xys = new Float32Array(len), | |
off = 0; | |
// concatenate all arrays to xys | |
this.selection.selectAll('div.tile') | |
.each(function() { if(this.array) { xys.set(this.array, off); offs.push(off); off += this.array.length } }); | |
ctx.data(xys); | |
var map = this; | |
if(map.timeout) { | |
clearTimeout(map.timeout); | |
} | |
map.timeout = setTimeout(function() { map.redraw() }, 100); | |
}, | |
render: function() | |
{ | |
var keys = []; | |
for(var key in this.arrays) | |
{ | |
keys.push(key); | |
} | |
var size = sq.Mouse.element_size(this.parent), | |
nw = this.pointLocation({x: 0, y: 0}), | |
se = this.pointLocation(size), | |
ul = this.projection.project(nw), | |
lr = this.projection.project(se); | |
ctx.draw(size, ul, lr); | |
}, | |
exit_handler: function(tile, node) | |
{ | |
this.queue.cancel(node); | |
var map = this; | |
if(map.timeout) { | |
clearTimeout(map.timeout); | |
} | |
map.timeout = setTimeout(function() { map.update() }, 25); | |
}, | |
enter_handler: function(tile, node) | |
{ | |
if(tile.coord.zoom < 12) | |
{ | |
return; | |
} | |
var map = this, | |
worker = map.workers[Math.floor(Math.random() * 2)]; | |
var callback = function(data) | |
{ | |
map.queue.close(node); | |
worker.postMessage({node_id: node.id, features: data['features'], zoom: tile.coord.zoom}); | |
} | |
node.id = this.next_int().toString(); | |
node.onjson = callback; | |
this.queue.append(node, 'http://tile.openstreetmap.us/vectiles-highroad/'+tile.toKey()+'.json'); | |
}, | |
new_data: function(node_id, list, elapsed) | |
{ | |
var f32array = new Float32Array(list); | |
this.selection.selectAll('div.tile') | |
.each(function() { if(this.id == node_id) { this.array = f32array } }); | |
var map = this; | |
if(map.timeout) { | |
clearTimeout(map.timeout); | |
} | |
map.timeout = setTimeout(function() { map.update() }, 25); | |
}, | |
next_int: function() | |
{ | |
if(this.number == undefined) | |
{ | |
this.number = 0; | |
} | |
return ++this.number; | |
} | |
} | |
function tile_key(tile) { return tile.toKey() } | |
function tile_left(tile) { return tile.left() } | |
function tile_top(tile) { return tile.top() } | |
function tile_width(tile) { return tile.width() } | |
function tile_height(tile) { return tile.height() } | |
function tile_xform(tile) { return tile.transform() } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body | |
{ | |
background-color: #EEE8D5; | |
margin: 0 !important; | |
} | |
#map | |
{ | |
width: 960px; | |
height: 500px; | |
position: relative; | |
overflow: hidden; | |
margin: 0; | |
padding: 0 0 0 0; | |
} | |
div.tile | |
{ | |
color: #839496; | |
display: block; | |
} | |
iframe | |
{ | |
width: 40px !important; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Queue() | |
{ | |
this.queue = []; | |
this.queue_by_id = {}; | |
this.open_request_count = 0; | |
this.requests_by_id = {}; | |
} | |
Queue.prototype = { | |
append: function(node, href) | |
{ | |
var request = new Request(node, href); | |
this.queue.push(request); | |
this.queue_by_id[request.id] = request; | |
}, | |
cancel: function(node) | |
{ | |
this.close(node); | |
var request = this.queue_by_id[node.id]; | |
if(request) | |
{ | |
request.deny(); | |
delete this.queue_by_id[node.id]; | |
} | |
}, | |
close: function(node) | |
{ | |
var request = this.requests_by_id[node.id]; | |
if(request) | |
{ | |
request.deny(); | |
delete this.requests_by_id[node.id]; | |
this.open_request_count--; | |
} | |
}, | |
process: function() | |
{ | |
//this.queue.sort(Request.prototype.compare); | |
//console.log('processing', this.open_request_count, 'open req count', this.queue.length, 'queue'); | |
while(this.open_request_count < 4 && this.queue.length > 0) | |
{ | |
var request = this.queue.shift(), | |
loading = request.load(); | |
if(loading) | |
{ | |
this.requests_by_id[request.id] = request; | |
this.open_request_count++; | |
} | |
delete this.queue_by_id[request.id]; | |
} | |
} | |
}; | |
function Request(node, href) | |
{ | |
this.id = node.id; | |
this.sort = node.sort; | |
this.node = node; | |
this.href = href; | |
} | |
Request.prototype = { | |
deny: function() | |
{ | |
this.node = null; | |
}, | |
load: function() | |
{ | |
if(this.node && this.node.parentNode) | |
{ | |
d3.json(this.href, this.node.onjson); | |
return true; | |
} | |
return false; | |
}, | |
compare: function(a, b) | |
{ | |
return b.sort - a.sort; | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment