Skip to content

Instantly share code, notes, and snippets.

@mappingvermont
Last active October 3, 2017 15:03
Show Gist options
  • Save mappingvermont/9bda617b9a35b0c4f2140d48b41368b8 to your computer and use it in GitHub Desktop.
Save mappingvermont/9bda617b9a35b0c4f2140d48b41368b8 to your computer and use it in GitHub Desktop.
TCD Tile Viewer - 2000 vs 2010
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>TCD Tile Comparison</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<!-- Load Leaflet from CDN-->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/leaflet/0.7.7/leaflet.css" />
<script src="https://cdn.jsdelivr.net/leaflet/0.7.7/leaflet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-hash/0.2.1/leaflet-hash.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://gist.githubusercontent.com/mappingvermont/9bda617b9a35b0c4f2140d48b41368b8/raw/8ab5b4d1ed17065da5cd36de962d0701c7e51589/style.css">
<script type="text/javascript" src="https://gist.githubusercontent.com/mappingvermont/9bda617b9a35b0c4f2140d48b41368b8/raw/8ab5b4d1ed17065da5cd36de962d0701c7e51589/canvas-layer.js"></script>
<script type="text/javascript" src="https://gist.githubusercontent.com/mappingvermont/9bda617b9a35b0c4f2140d48b41368b8/raw/8ab5b4d1ed17065da5cd36de962d0701c7e51589/script.js"></script>
</head>
<body onLoad="init();">
<div id="map"></div>
</body>
</html>
/**
If possible, I would avoid looking in here/editing any of the code
It was written by Gerardo Pacheco, and is fairly complex with respect
to scaling GLAD map tiles dynamically.
I've commented out the tile cache stuff-- was interfering with my poor
implementation of dynamic tile filtering-- but otherwise this code is as
he wrote it.
Ideally this should be included in the project and then referenced only as it
is in script.js-- using the getTile method to overwrite Leaflet's CanvasLayer drawTile
*/
function Canvas(options) {
this.dataMaxZoom = options.maxZoom || 12;
this.tiles = {};
this.urlTemplate = options.urlTemplate || '';
}
Canvas.prototype.getTile = function(coord, zoom, canvas) {
/**
* Enable cache of tiles
*/
//var tileId = this._getTileId(coord.x, coord.y, zoom);
//var objKeys = Object.keys(this.tiles);
//for (var i = 0; i < objKeys.length; i++) {
// if (this.tiles[objKeys[i]].z !== zoom) {
// delete this.tiles[objKeys[i]];
// }
//}
//if (this.tiles[tileId]) {
// return this.tiles[tileId].canvas;
//}
var url = this._getUrl.apply(this, this._getTileCoords(coord.x, coord.y, zoom));
this._getImage(url, function(image) {
var canvasData = {
canvas: canvas,
image: image,
x: coord.x,
y: coord.y,
z: zoom
};
//this._cacheTile(canvasData);
this._drawCanvasImage(canvasData);
}.bind(this));
return canvas;
};
Canvas.prototype._getImage = function(url, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var url = URL.createObjectURL(this.response);
var image = new Image();
image.onload = function() {
image.crossOrigin = '';
callback(image);
URL.revokeObjectURL(url);
};
image.src = url;
};
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.send();
};
Canvas.prototype._getTileId = function(x, y, z) {
return x + '_' + y + '_' + z;
};
Canvas.prototype._getZoomSteps = function(z) {
return z - this.dataMaxZoom;
};
Canvas.prototype.pad = function(num) {
var s = '00' + num;
return s.substr(s.length - 3);
};
Canvas.prototype.filterTileImgdata = function(data) {
for (var i = 0; i < data.length; i += 4) {
var rgba = data.slice(i, i + 4)
var intensityBand = 1
// if the pixel passes the date + conf filters, recolor it in some way
if (rgba[intensityBand] > 0) {
var intensity = rgba[intensityBand] * 0.8
if (intensity > 255) {
intensity = 255
}
// this just turns all pixels green
data[i] = 151
data[i + 1] = 189,
data[i + 2] = 61
data[i + 3] = intensity
// if it doesn't match the filter, set opacity to 0
} else {
data[i + 3] = 0
}
}
return data;
};
Canvas.prototype._drawCanvasImage = function(canvasData) {
"use asm";
var canvas = canvasData.canvas,
ctx = canvas.getContext('2d'),
image = canvasData.image,
zsteps = this._getZoomSteps(canvasData.z) | 0; // force 32bit int type
ctx.clearRect(0, 0, 256, 256); // this will allow us to sum up the dots when the timeline is running
if (zsteps < 0) {
ctx.drawImage(image, 0, 0);
} else { // over the maxzoom, we'll need to scale up each tile
ctx.imageSmoothingEnabled = false; // disable pic enhancement
ctx.mozImageSmoothingEnabled = false;
// tile scaling
var srcX = (256 / Math.pow(2, zsteps) * (canvasData.x % Math.pow(2, zsteps))) |0,
srcY = (256 / Math.pow(2, zsteps) * (canvasData.y % Math.pow(2, zsteps))) |0,
srcW = (256 / Math.pow(2, zsteps)) |0,
srcH = (256 / Math.pow(2, zsteps)) |0;
ctx.drawImage(image, srcX, srcY, srcW, srcH, 0, 0, 256, 256);
}
var I = ctx.getImageData(0, 0, canvas.width, canvas.height);
this.filterTileImgdata(I.data);
ctx.putImageData(I, 0, 0);
};
Canvas.prototype._getUrl = function(x, y, z) {
return this.urlTemplate.replace('%z', z).replace('%x', x).replace('%y', y);
};
Canvas.prototype._getTileCoords = function(x, y, z) {
if (z > this.dataMaxZoom) {
x = Math.floor(x / (Math.pow(2, z - this.dataMaxZoom)));
y = Math.floor(y / (Math.pow(2, z - this.dataMaxZoom)));
z = this.dataMaxZoom;
} else {
y = (y > Math.pow(2, z) ? y % Math.pow(2, z) : y);
if (x >= Math.pow(2, z)) {
x = x % Math.pow(2, z);
} else if (x < 0) {
x = Math.pow(2, z) - Math.abs(x);
}
}
return [x, y, z];
};
Canvas.prototype._cacheTile = function(canvasData) {
var tileId = this._getTileId(canvasData.x, canvasData.y, canvasData.z);
canvasData.canvas.setAttribute('id', tileId);
this.tiles[tileId] = canvasData;
};
function init() {
// create canvas layer place holder
var tcd2000Layer = new Canvas({
maxZoom: 12,
urlTemplate: 'http://earthengine.google.org/static/hansen_2014/gfw_loss_tree_year_30_2014/%z/%x/%y.png'
});
// create canvas layer place holder
var tcd2010Layer = new Canvas({
maxZoom: 12,
//urlTemplate: 'http://storage.googleapis.com/wri-public/treecover2010/thresholds/30/%z/%x/%y'
//urlTemplate: 'https://storage.googleapis.com/wri-public/treecover2010/tiles/thresholds/30/%z/%x/%y.png'
//urlTemplate: 'https://storage.googleapis.com/wri-public/treecover2000/tiles/thresholds/30/%z/%x/%y.png'
//urlTemplate: 'https://storage.googleapis.com/wri-public/treecover2000-test/tiles/thresholds/30/%z/%x/%y.png'
//urlTemplate: 'https://storage.googleapis.com/wri-public/treecover2000/30/%z/%x/%y.png'
urlTemplate: 'https://storage.googleapis.com/wri-public/treecover2010/30/%z/%x/%y.png'
});
var tcd2000 = L.tileLayer.canvas({noWrap: true});
var tcd2010 = L.tileLayer.canvas({noWrap: true});
// set bounding box for map + create it
var southWest = L.latLng(-90, -179),
northEast = L.latLng(90, 179),
worldBounds = L.latLngBounds(southWest, northEast);
var map = L.map('map', {
noWrap: true,
minZoom: 3,
maxZoom: 16,
maxBounds: worldBounds
}).setView([0, 15], 3);
// initialize the Leaflet hash plugin to add zoom/lat/lon hash to our url
var hash = new L.Hash(map);
// add the OSM basemap
var osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
});
map.addLayer(osm);
tcd2000.drawTile = function(canvas, tilePoint, zoom) {
// pass these and the confidence to the custom getTile method so we can filter GLAD
tcd2000Layer.getTile(tilePoint, zoom, canvas);
};
tcd2000.addTo(map);
tcd2010.drawTile = function(canvas, tilePoint, zoom) {
// pass these and the confidence to the custom getTile method so we can filter GLAD
tcd2010Layer.getTile(tilePoint, zoom, canvas);
};
L.control.layers({'TCD 2000 - Original': tcd2000, 'TCD 2000 - New': tcd2010}, null).addTo(map);
}
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment