Skip to content

Instantly share code, notes, and snippets.

@springmeyer
Forked from willwhite/gist:790163
Created January 21, 2011 19:05
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 springmeyer/790212 to your computer and use it in GitHub Desktop.
Save springmeyer/790212 to your computer and use it in GitHub Desktop.
// command line test:
#!/usr/bin/env node
var SphericalMercator = require('./lib/tilelive/sphericalmercator.js')
var merc = new SphericalMercator({})
min_zoom = 1;
max_zoom = 2;
// if we have a merc extent
extent = [-20037508.34,-20037508.34,20037508.34,20037508.34];
merc.envelope_to_xyz_array(extent,min_zoom,max_zoom)
// if we have a long/lat bbox
bbox = [-179.9,-60,179.9,60]
merc.bbox_to_xyz_array(bbox,min_zoom,max_zoom)
// spherical mercator impl:
var mapnik = require('mapnik');
var mercator = new mapnik.Projection('+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs +over');
var _ = require('underscore')._;
/**
* SphericalMercator constructor: precaches calculations
* for fast tile lookups
*/
function SphericalMercator(options) {
var size = options.size || 256;
this.Bc = [];
this.Cc = [];
this.zc = [];
this.Ac = [];
this.DEG_TO_RAD = Math.PI / 180;
this.RAD_TO_DEG = 180 / Math.PI;
this.size = options.size || 256;
this.levels = options.levels || 18;
for (var d = 0; d < this.levels; d++) {
this.Bc.push(size / 360);
this.Cc.push(size / (2 * Math.PI));
this.zc.push(size / 2);
this.Ac.push(size);
size *= 2;
}
}
/**
* Get the max of the first two numbers and the min of that and the third
*
* @param {Number} a the first number.
* @param {Number} b the second number.
* @param {Number} c the third number.
* @return {Number}
*/
SphericalMercator.prototype.minmax = function(a, b, c) {
return Math.min(Math.max(a, b), c);
};
/**
* Convert lat lon to screen pixel value
*
* @param {Array} px [lat lon] array of geographic coordinates.
* @param {Number} zoom number of the zoom level.
*/
SphericalMercator.prototype.ll_to_px = function(ll, zoom) {
var d = this.zc[zoom];
var f = this.minmax(Math.sin(this.DEG_TO_RAD * ll[1]), -0.9999, 0.9999);
var x = Math.round(d + ll[0] * this.Bc[zoom]);
var y = Math.round(d + 0.5 * Math.log((1 + f) / (1 - f)) * (-this.Cc[zoom]));
return [x, y];
};
/**
* Convert screen pixel value to lat lon
*
* @param {Array} px [x y] array of geographic coordinates.
* @param {Number} zoom number of the zoom level.
*/
SphericalMercator.prototype.px_to_ll = function(px, zoom) {
var zoom_denom = this.zc[zoom];
var g = (px[1] - zoom_denom) / (-this.Cc[zoom]);
var lat = (px[0] - zoom_denom) / this.Bc[zoom];
var lon = this.RAD_TO_DEG * (2 * Math.atan(Math.exp(g)) - 0.5 * Math.PI);
return [lat, lon];
};
/**
* Convert tile xyz value to Mapnik envelope
*
* @param {Number} x latitude number.
* @param {Number} y longitude number.
* @param {Number} zoom zoom.
* @param {Boolean} tms_style whether to compute a tms tile.
* @return Object Mapnik envelope.
*/
SphericalMercator.prototype.xyz_to_envelope = function(x, y, zoom, tms_style) {
if (tms_style) {
y = (Math.pow(2, zoom) - 1) - y;
}
var ll = [x * this.size, (y + 1) * this.size];
var ur = [(x + 1) * this.size, y * this.size];
var bbox = this.px_to_ll(ll, zoom).concat(this.px_to_ll(ur, zoom));
var env = mercator.forward(bbox);
return env;
};
// assumes an envelope is in projected coords, in this case spherical mercator
SphericalMercator.prototype.envelope_to_xyz_array = function(envelope, minzoom, maxzoom) {
var bbox = mercator.inverse(envelope);
this.bbox_to_xyz_array(bbox,minzoom,maxzoom);
}
// assumes at bbox is in long/lat aka wgs 84
SphericalMercator.prototype.bbox_to_xyz_array = function(bbox, minzoom, maxzoom) {
var ll0 = [bbox[0],bbox[3]]
var ll1 = [bbox[2],bbox[1]]
var that = this;
_.range(minzoom, maxzoom+1).forEach(function(z) {
px0 = that.ll_to_px(ll0,z);
px1 = that.ll_to_px(ll1,z);
_.range(parseInt(px0[0]/256.0), parseInt(px1[0]/256.0+1)).forEach(function(x) {
_.range(parseInt(px0[1]/256.0), parseInt(px1[1]/256.0+1)).forEach(function(y) {
console.log(z + ' ' + x + ' ' + y);
})
})
});
}
module.exports = SphericalMercator;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment