Last active
January 2, 2016 15:19
-
-
Save andrewxhill/8323177 to your computer and use it in GitHub Desktop.
example.html from mapshot
This file contains 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> | |
<script src="mapshot.js"></script> | |
<script src="http://libs.cartocdn.com/cartodb.js/v3/cartodb.core.js"></script> | |
<script> | |
// register cartodb | |
mapshot.register('cartodb', function(data, done) { | |
cartodb.Tiles.getTiles({ | |
user_name: data.user_name, | |
sublayers: data.sublayers | |
}, function(data) { | |
done(data.tiles[0].replace('{s}', 1)); | |
}); | |
}); | |
var m = mapshot.map({ | |
center: [40, -4], | |
zoom: 4, | |
width: 700, | |
height: 300, | |
renderer_type: 'canvas', | |
layers: [ | |
'http://tile.stamen.com/toner/{z}/{x}/{y}.png', | |
//'http://{s}.tiles.mapbox.com/v3/examples.map-vyofok3q/{z}/{x}/{y}.png', | |
{ | |
type: 'cartodb', | |
user_name: 'examples', | |
sublayers: [{ | |
sql: 'select * from ne_10m_populated_p_2', | |
cartocss: '#ne_10m_populated_p_2{ marker-fill: #F11810; marker-opacity: 0.9; marker-allow-overlap: true; marker-placement: point; marker-type: ellipse; marker-width: 7.5; marker-line-width: 2; marker-line-color: #000; marker-line-opacity: 0.2; }' | |
}] | |
} | |
] | |
}) | |
m.renderer() | |
.addPass('blur', { passes: 20, center:[40, -4], dist: 60}) | |
.addPass('noise', 10) | |
.addPass('posterize', 35) | |
m.render(); | |
</script> | |
</body> |
This file contains 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() { | |
var root = this; | |
var layerTypes = { | |
'template': function(data, fn) { fn(data); } | |
}; | |
function map(opts) { | |
var m = new Map(opts); | |
document.write(m._renderer.el.outerHTML); | |
m._renderer.el = document.getElementById(m._renderer.id); | |
return m; | |
} | |
function register(type, fn) { | |
layerTypes[type] = fn; | |
} | |
/** | |
* mercator projecttion | |
*/ | |
function MercatorProjection() { | |
this.TILE_SIZE = 256; | |
this.pixelOrigin_ = { x: this.TILE_SIZE / 2, y: this.TILE_SIZE / 2 }; | |
this.pixelsPerLonDegree_ = this.TILE_SIZE / 360; | |
this.pixelsPerLonRadian_ = this.TILE_SIZE / (2 * Math.PI); | |
} | |
MercatorProjection.prototype.toPixelCoordinate = function(worldCoordinate, zoom) { | |
var numTiles = 1 << zoom; | |
return { | |
x: worldCoordinate.x * numTiles, | |
y: worldCoordinate.y * numTiles | |
} | |
} | |
MercatorProjection.prototype.fromLatLngToPoint = function(latLng, | |
opt_point) { | |
var me = this; | |
var point = opt_point || { x:0, y: 0 }; | |
var origin = me.pixelOrigin_; | |
point.x = origin.x + latLng[1] * me.pixelsPerLonDegree_; | |
// NOTE(appleton): Truncating to 0.9999 effectively limits latitude to | |
// 89.189. This is about a third of a tile past the edge of the world | |
// tile. | |
var siny = bound(Math.sin(degreesToRadians(latLng[0])), -0.9999, | |
0.9999); | |
point.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) * | |
-me.pixelsPerLonRadian_; | |
return point; | |
}; | |
function bound(value, opt_min, opt_max) { | |
if (opt_min != null) value = Math.max(value, opt_min); | |
if (opt_max != null) value = Math.min(value, opt_max); | |
return value; | |
} | |
function degreesToRadians(deg) { | |
return deg * (Math.PI / 180); | |
} | |
MercatorProjection.prototype.pixelToTile = function(pixelCoordinate) { | |
return { | |
x: Math.floor(pixelCoordinate.x / this.TILE_SIZE), | |
y: Math.floor(pixelCoordinate.y / this.TILE_SIZE) | |
}; | |
}; | |
MercatorProjection.prototype.fromLatLngToPixel = function(latLng, zoom) { | |
var p = this.fromLatLngToPoint(latLng); | |
return this.toPixelCoordinate(p, zoom); | |
}; | |
function Map(opts) { | |
opts = opts || {}; | |
opts.renderer_type = opts.renderer_type || 'dom'; | |
this.options = opts; | |
this.projection = new MercatorProjection() || opts.projection; | |
this._renderer = new renderers[opts.renderer_type](this.options.width, this.options.height); | |
this._renderer.map = this; | |
} | |
Map.prototype.fetchLayers = function(done) { | |
this.layers = []; | |
var count = 0; | |
var self = this; | |
for(var lyr = 0; lyr < this.options.layers.length; ++lyr) { | |
var layer = this.options.layers[lyr]; | |
var type = layer.type; | |
if(typeof(layer) === 'string') { | |
type = 'template'; | |
} | |
layerTypes[type]( | |
layer, | |
(function(i) { | |
return function(tmpl) { | |
self.layers[i] = tmpl; | |
++count; | |
if(count == self.options.layers.length) { | |
done.call(self); | |
} | |
}; | |
})(lyr) | |
); | |
} | |
}; | |
Map.prototype.visibleTiles = function() { | |
width = this.options.width; | |
height = this.options.height; | |
var widthHalf = width / 2; | |
var heightHalf = height / 2; | |
var center_point = this.projection.fromLatLngToPixel(this.options.center, this.options.zoom); | |
center_point.x -= widthHalf; | |
center_point.y -= heightHalf; | |
var tile = this.projection.pixelToTile(center_point, this.zoom); | |
var offset_x = center_point.x%this.projection.TILE_SIZE; | |
var offset_y = center_point.y%this.projection.TILE_SIZE; | |
var num_tiles_x = Math.ceil((width + offset_x)/this.projection.TILE_SIZE); | |
var num_tiles_y = Math.ceil((height + offset_y)/this.projection.TILE_SIZE); | |
var tiles = []; | |
for(var i = 0; i < num_tiles_x; ++i) { | |
for(var j = 0; j < num_tiles_y; ++j) { | |
var tile_x = tile.x + i; | |
var tile_y = tile.y + j; | |
tiles.push({ | |
x: tile_x * this.projection.TILE_SIZE, | |
y: tile_y * this.projection.TILE_SIZE, | |
zoom: this.options.zoom, | |
i: tile_x, | |
j: tile_y | |
}); | |
} | |
} | |
var self = this; | |
// by distance to center | |
tiles.sort(function(a, b) { | |
var ox = (widthHalf/self.projection.TILE_SIZE)|0; | |
var oy = (heightHalf/self.projection.TILE_SIZE)|0; | |
var da = Math.abs(a.i - tile.x - ox ) + Math.abs(a.j - tile.y -oy); | |
var db = Math.abs(b.i - tile.x - ox) + Math.abs(b.j - tile.y -oy); | |
return da - db; | |
}); | |
return tiles; | |
}; | |
function DomRenderer(w, h) { | |
var el = this.el = document.createElement('div'); | |
var id = "pxmap_" + new Date().getTime(); | |
this.id = id; | |
el.setAttribute('id', id); | |
el.style.position = 'relative'; | |
el.style.overflow = 'hidden'; | |
el.style.width = w + "px"; | |
el.style.height = h + "px"; | |
} | |
DomRenderer.prototype.renderTile = function(img_url, x, y, TILE_SIZE) { | |
var el = this.el; | |
var img = new Image(); | |
img.crossOrigin = ""; | |
img.src = img_url; | |
img.style.position = 'absolute'; | |
img.style.left = x + "px"; | |
img.style.top = y + "px"; | |
img.style.width = TILE_SIZE + "px"; | |
img.style.height = TILE_SIZE + "px"; | |
el.appendChild(img); | |
}; | |
function perpixel(imagedata, w, h, options, fn) { | |
var pixels = imagedata.data; | |
for(var i = 0; i < w; ++i) { | |
for(var j = 0; j < h; ++j) { | |
var idx = 4*(j*w + i); | |
var r = pixels[idx + 0]; | |
var g = pixels[idx + 1]; | |
var b = pixels[idx + 2]; | |
var a = pixels[idx + 3]; | |
var rgba = fn([r, g, b, a], i, j) | |
r = rgba[0] | |
g = rgba[1] | |
b = rgba[2] | |
a = rgba[3] | |
//r *= 0.1; | |
//r = g = b = 40; | |
//a = 255; | |
//a *= 0.3 | |
pixels[idx + 0] = r; | |
pixels[idx + 1] = g; | |
pixels[idx + 2] = b; | |
pixels[idx + 3] = a; | |
} | |
} | |
} | |
function noise(pixels, w, h, qty) { | |
perpixel(pixels, w, h, null, function(rgba) { | |
var rand = (2*Math.random() - 1)*qty; | |
rgba[0] += rand; | |
rgba[1] += rand; | |
rgba[2] += rand; | |
return rgba; | |
}); | |
} | |
// from https://github.com/meltingice/CamanJS-Plugins/blob/f958b78efcf78fd888b7a60b9e07812a2b45eab2/src/posterize.coffee | |
function posterize(pixels, w, h, adjust) { | |
var numOfAreas = 256 / adjust; | |
var numOfValues = 255 / (adjust - 1); | |
perpixel(pixels, w, h, null, function(rgba) { | |
rgba[0] = Math.floor(Math.floor(rgba[0] / numOfAreas) * numOfValues); | |
rgba[1] = Math.floor(Math.floor(rgba[1] / numOfAreas) * numOfValues); | |
rgba[2] = Math.floor(Math.floor(rgba[2] / numOfAreas) * numOfValues); | |
return rgba; | |
}); | |
}; | |
function blur(pixels, w, h, options, map) { | |
options.passes = options.passes || 10; | |
var el = document.createElement('canvas'); | |
el.width = w; | |
el.height = h; | |
var ctx = el.getContext('2d'); | |
ctx.putImageData(pixels, 0, 0); | |
for(var i = 0; i < options.passes ; ++i) { | |
var small = document.createElement('canvas'); | |
small.width = w >> 1; | |
small.height = h >> 1; | |
small.getContext('2d').drawImage(el, 0, 0, small.width, small.height); | |
ctx.globalAlpha = 0.5; | |
ctx.drawImage(small, 0, 0, el.width, el.height); | |
} | |
if(options.center) { | |
var center = map.projection.fromLatLngToPixel(options.center, map.options.zoom); | |
var map_center = map.projection.fromLatLngToPixel(map.options.center, map.options.zoom); | |
center.x -= map_center.x - w/2; | |
center.y -= map_center.y - h/2; | |
} | |
// get and blend | |
var blurred = ctx.getImageData(0, 0, w, h).data; | |
perpixel(pixels, w, h, null, function(rgba, i, j) { | |
var factor = 1.0; | |
if (options.center) { | |
var dx = center.x - i; | |
var dy = center.y - j; | |
factor = Math.sqrt(dx*dx + dy*dy)/options.dist; | |
if(factor > 1) factor = 1.0; | |
//factor = 1; | |
} | |
var idx = 4*(j*w + i); | |
return [ | |
blurred[idx + 0] * factor + (1 - factor)*rgba[0], | |
blurred[idx + 1] * factor + (1 - factor)*rgba[1], | |
blurred[idx + 2] * factor + (1 - factor)*rgba[2], | |
blurred[idx + 3] * factor + (1 - factor)*rgba[3] | |
]; | |
}); | |
} | |
function CanvasRenderer(w, h) { | |
this.tiles = []; | |
this.loaded = 0; | |
this.processorTypes = { | |
'noise': noise, | |
'blur': blur, | |
'posterize': posterize | |
}; | |
this.processor = []; | |
this.w = w; | |
this.h = h; | |
var el = this.el = document.createElement('canvas'); | |
var id = "pxmap_" + new Date().getTime(); | |
this.id = id; | |
el.setAttribute('id', id); | |
el.width = w; | |
el.height = h; | |
} | |
CanvasRenderer.prototype.register = function(type, fn) { | |
this.processorTypes[type] = fn; | |
}; | |
CanvasRenderer.prototype.addPass = function(type, opts){ | |
this.processor.push({ | |
type: type, | |
options: opts | |
}); | |
return this; | |
}; | |
CanvasRenderer.prototype.render = function() { | |
var ctx = this.el.getContext('2d'); | |
for(var i = 0; i < this.tiles.length; ++i) { | |
var t = this.tiles[i]; | |
ctx.drawImage(t.img, t.x, t.y); | |
} | |
var idata = ctx.getImageData(0, 0, this.w, this.h); | |
for(var i = 0; i < this.processor.length; ++i) { | |
var p = this.processor[i]; | |
this.processorTypes[p.type](idata, this.w, this.h, p.options, this.map); | |
}; | |
ctx.putImageData(idata, 0, 0); | |
}; | |
CanvasRenderer.prototype.renderTile = function(img_url, x, y, TILE_SIZE) { | |
var self = this; | |
var el = this.el; | |
var img = new Image(); | |
img.crossOrigin = ""; | |
img.onload = function() { | |
++self.loaded; | |
if(self.tiles.length == self.loaded) { | |
self.render(); | |
} | |
} | |
this.tiles.push({ img: img, x: x, y: y }); | |
img.src = img_url; | |
}; | |
var renderers = { | |
'dom': DomRenderer, | |
'canvas': CanvasRenderer | |
} | |
Map.prototype.renderer = function() { | |
return this._renderer; | |
} | |
Map.prototype.render = function() { | |
var tiles = this.visibleTiles(); | |
var center_point = this.projection.fromLatLngToPixel(this.options.center, this.options.zoom); | |
center_point.x -= this.options.width/2; | |
center_point.y -= this.options.height/2; | |
this.fetchLayers(function() { | |
for(var lyr = 0; lyr < this.layers.length; ++lyr) { | |
var layer = this.layers[lyr]; | |
var template = layer; | |
var subdomains = 'abcd'; | |
for(var i = 0; i < tiles.length; ++i) { | |
var tile = tiles[i]; | |
var img = template | |
.replace('{s}', 'abcd'[(tile.i + tile.j) % 4]) | |
.replace('{x}', tile.i) | |
.replace('{y}', tile.j) | |
.replace('{z}', tile.zoom); | |
this._renderer.renderTile(img, | |
tile.x - center_point.x, | |
tile.y - center_point.y, | |
this.projection.TILE_SIZE | |
); | |
} | |
} | |
}); | |
return this; | |
}; | |
root.mapshot = { | |
map: map, | |
register: register, | |
Map: Map | |
}; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment