Skip to content

Instantly share code, notes, and snippets.

@andrewxhill
Last active January 2, 2016 15:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewxhill/8323177 to your computer and use it in GitHub Desktop.
Save andrewxhill/8323177 to your computer and use it in GitHub Desktop.
example.html from mapshot
<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>
(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