block by andrewxhill 8323177

example.html from mapshot

Full Screen

index.html

<body>
<script src="mapshot.js"></script>
<script src="//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: [
      '//tile.stamen.com/toner/{z}/{x}/{y}.png',
      //'//{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>

mapshot.js

(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
  };

}());