UNPKG

tessel-map

Version:

minimal meta map framework

823 lines (700 loc) 25.1 kB
;(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){ var tessel = require('../'), tesselCanvas = require('tessel-canvas'); var map = tessel.map(); var canvas = document.body.appendChild(document.createElement('canvas')); canvas.width = 500; canvas.height = 500; tesselCanvas(map.state(), canvas); },{"../":2,"tessel-canvas":3}],3:[function(require,module,exports){ // TODO: use a module function asyncMap(inputs, func, callback) { var remaining = inputs.length, results = [], errors = []; inputs.forEach(function(d, i) { func(d, function done(err, data) { errors[i] = err; results[i] = data; remaining --; if (!remaining) callback(errors, results); }); }); } function draw(state, canvas, cb) { var ctx = canvas.getContext('2d'); asyncMap(state.tiles, function(t, cb) { var im = new Image(); im.onload = im.onerror = function() { t.img = this; cb(null, t); }; im.src = t.src; }, function(err, results) { results.forEach(function(r) { ctx.drawImage(r.img, r.dx, r.dy, r.w, r.h); }); if (cb) cb(); }); } module.exports = draw; },{}],2:[function(require,module,exports){ var sph = require('sphericalmercator'), mercator = new sph(), animator = require('animator'), Coordinate = require('tessel-coordinate'), translate = require('css3-translate'); function template(str) { return function(t) { return str .replace(/\{z\}/g, t.zoom) .replace(/\{x\}/g, t.column) .replace(/\{y\}/g, t.row); }; } module.exports.template = template; module.exports.map = function() { var coord = new Coordinate(8, 8, 4), tileSize = [256, 256]; dimensions = [500, 500]; function pointCoordinate(point) { var c = coord.copy(); c.column += (point[0] - dimensions[0] / 2) / tileSize[0]; c.row += (point[1] - dimensions[1] / 2) / tileSize[1]; return c; } function tilesVisible() { var start = pointCoordinate([0, 0]).zoomTo(coord.zoom).container(), end = pointCoordinate(dimensions).zoomTo(coord.zoom) .container().right().down(), i = start.copy(), coords = []; for (i.column = start.column; i.column < end.column; i.column++) { for (i.row = start.row; i.row < end.row; i.row++) coords.push(i.copy()); } return coords; } var defaultTemplate = template('http://a.tiles.mapbox.com/v3/tmcw.map-1rgcfrpf/{z}/{x}/{y}.png'); function tilesPositioned(tiles) { var center = [dimensions[0] / 2, dimensions[1] / 2]; return tiles.map(function(t) { return { coord: t, dx: Math.round(center[0] + (t.column - coord.column) * tileSize[0]), dy: Math.round(center[1] + (t.row - coord.row) * tileSize[1]), src: defaultTemplate(t), w: tileSize[0], h: tileSize[1] }; }); } var map = {}; map.state = function() { return { tiles: tilesPositioned(tilesVisible()), dimensions: dimensions }; }; map.dimensions = function() { if (!arguments.length) return dimensions; else { dimensions = arguments[0]; return map; } }; return map; }; },{"tessel-coordinate":4,"css3-translate":5,"sphericalmercator":6,"animator":7}],4:[function(require,module,exports){ // Coordinate // ---------- // An object representing a tile position, at as specified zoom level. // This is not necessarily a precise tile - `row`, `column`, and // `zoom` can be floating-point numbers, and the `container()` function // can be used to find the actual tile that contains the point. function Coordinate(row, column, zoom) { this.row = row; this.column = column; this.zoom = zoom; } Coordinate.prototype = { row: 0, column: 0, zoom: 0, // Quickly generate a string representation of this coordinate to // index it in hashes. toKey: function() { // We've tried to use efficient hash functions here before but we took // them out. Contributions welcome but watch out for collisions when the // row or column are negative and check thoroughly (exhaustively) before // committing. return this.zoom + ',' + this.row + ',' + this.column; }, copy: function() { return new Coordinate(this.row, this.column, this.zoom); }, eq: function(other) { return this.row === other.row && this.column === other.column && this.zoom === other.zoom; }, // Get the actual, rounded-number tile that contains this point. container: function() { // using floor here (not parseInt, ~~) because we want -0.56 --> -1 return new Coordinate(Math.floor(this.row), Math.floor(this.column), Math.floor(this.zoom)); }, // Recalculate this Coordinate at a different zoom level and return the // new object. zoomTo: function(destination) { var power = Math.pow(2, destination - this.zoom); return new Coordinate(this.row * power, this.column * power, destination); }, // Recalculate this Coordinate at a different relative zoom level and return the // new object. zoomBy: function(distance) { var power = Math.pow(2, distance); return new Coordinate(this.row * power, this.column * power, this.zoom + distance); }, // Move this coordinate up by `dist` coordinates up: function(dist) { if (dist === undefined) dist = 1; return new Coordinate(this.row - dist, this.column, this.zoom); }, // Move this coordinate right by `dist` coordinates right: function(dist) { if (dist === undefined) dist = 1; return new Coordinate(this.row, this.column + dist, this.zoom); }, // Move this coordinate down by `dist` coordinates down: function(dist) { if (dist === undefined) dist = 1; return new Coordinate(this.row + dist, this.column, this.zoom); }, // Move this coordinate left by `dist` coordinates left: function(dist) { if (dist === undefined) dist = 1; return new Coordinate(this.row, this.column - dist, this.zoom); } }; module.exports = Coordinate; },{}],5:[function(require,module,exports){ /** * A tiny, functional CSS3 translation transforms API for JavaScript. * * References: * * https://github.com/component/transform-property * https://developer.mozilla.org/en-US/docs/CSS/transform-function#translate() * * @license MIT license <http://mattcg.mit-license.org/> * @author Matthew Caruana Galizia <m@m.cg> * @copyright Copyright (c) 2013, Matthew Caruana Galizia */ /*jshint node:true, browser:true*/ 'use strict'; /** * @private * @param {Element} el * @return {string} */ var rule = function(el) { var found, styles, style, value, i, l; // The determination logic is only run once. After that, `rule` is reassigned to a new function that just returns the found rule name. rule = function() { return found; }; // This portion of code was derived from: https://github.com/component/transform-property styles = [ 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform', 'transform' ]; for (i = 0, l = styles.length; i < l; i++) { style = styles[i]; value = el.style[style]; if (undefined !== value) { found = style; return found; } } }; /** * @private * @param {string|number} value */ var units = function(value) { if (!value) { return 0; } if (/[0-9]/.test(String(value).substr(-1))) { return value + 'px'; } return value; }; /** * @private * @param {Element} el * @param {string} value */ var apply = function(el, value) { el.style[rule(el)] = value; }; /** * Apply a `translate(tx, ty)` CSS transform to the given element, moving the position of the element on the plane. * * If a length does not include units, `px` will be used. * * @param {Element} el * @param {string|number} [tx=0] A length representing the abscissa of the translating vector. * @param {string|number} ty A length representing the ordinate of the translating vector. If missing, it is assumed to be equal to tx: `translate(2)` means `translate(2, 2)`. */ var translate = function(el, tx, ty) { if (!ty && ty !== 0) { ty = tx; } apply(el, 'translate(' + units(tx) + ', ' + units(ty) + ')'); }; /** * Apply a `translate3d(tx, ty, tz)` CSS transform to the given element, moving the position of the element in the 3D space. * * If a length does not include units, `px` will be used. * * @param {Element} el * @param {string|number} [tx=0] A length representing the abscissa of the translating vector. * @param {string|number} [ty=0] A length representing the ordinate of the translating vector. * @param {string|number} [tz=0] A length representing the z component of the translating vector. It can't be a percentage value; in that case the property containing the transform is considered invalid. */ translate.d3 = translate['3d'] = function(el, tx, ty, tz) { apply(el, 'translate3d(' + units(tx) + ', ' + units(ty) + ', ' + units(tz) + ')'); }; /** * Apply a `translateX(tx)` CSS transform to the given element, moving the element horizontally on the plane. * * `translate.x(tx)` is a shortcut for `translate(tx, 0)`. * * If the length does not include units, `px` will be used. * * @param {Element} el * @param {string|number} [tx=0] A length representing the abscissa of the translating vector. */ translate.x = function(el, tx) { apply(el, 'translateX(' + units(tx) + ')'); }; /** * Apply a `translateY(ty)` CSS transform to the given element, moving the element vertically on the plane. * * `translate.y(ty)` is a shortcut for `translate(0, ty)`. * * If the length does not include units, `px` will be used. * * @param {Element} el * @param {string|number} [ty=0] A length representing the ordinate of the translating vector. */ translate.y = function(el, ty) { apply(el, 'translateY(' + units(ty) + ')'); }; /** * Apply a `translateZ(tz)` CSS transform to the given element, moving the element along the z-axis of the 3D space. * * `translate.z(tz)` is a shortcut for `translate3d(0, 0, tz)`. * * If the length does not include units, `px` will be used. It can't be a percentage value; in that case the property containing the transform is considered invalid. * * @param {Element} el * @param {string|number} [tz=0] A length representing the z-component of the translating vector. */ translate.z = function(el, tz) { apply(el, 'translateZ(' + units(tz) + ')'); }; /** * Get the browser-specific transform rule e.g. 'webkitTransform' or 'transform'. * * @return {string} */ translate.rule = function() { return rule(document.documentElement); }; module.exports = translate; },{}],6:[function(require,module,exports){ var SphericalMercator = (function(){ // Closures including constants and other precalculated values. var cache = {}, EPSLN = 1.0e-10, D2R = Math.PI / 180, R2D = 180 / Math.PI, // 900913 properties. A = 6378137, MAXEXTENT = 20037508.34; // SphericalMercator constructor: precaches calculations // for fast tile lookups. function SphericalMercator(options) { options = options || {}; this.size = options.size || 256; if (!cache[this.size]) { var size = this.size; var c = cache[this.size] = {}; c.Bc = []; c.Cc = []; c.zc = []; c.Ac = []; for (var d = 0; d < 30; d++) { c.Bc.push(size / 360); c.Cc.push(size / (2 * Math.PI)); c.zc.push(size / 2); c.Ac.push(size); size *= 2; } } this.Bc = cache[this.size].Bc; this.Cc = cache[this.size].Cc; this.zc = cache[this.size].zc; this.Ac = cache[this.size].Ac; }; // Convert lon lat to screen pixel value // // - `ll` {Array} `[lon, lat]` array of geographic coordinates. // - `zoom` {Number} zoom level. SphericalMercator.prototype.px = function(ll, zoom) { var d = this.zc[zoom]; var f = Math.min(Math.max(Math.sin(D2R * 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])); (x > this.Ac[zoom]) && (x = this.Ac[zoom]); (y > this.Ac[zoom]) && (y = this.Ac[zoom]); //(x < 0) && (x = 0); //(y < 0) && (y = 0); return [x, y]; }; // Convert screen pixel value to lon lat // // - `px` {Array} `[x, y]` array of geographic coordinates. // - `zoom` {Number} zoom level. SphericalMercator.prototype.ll = function(px, zoom) { var g = (px[1] - this.zc[zoom]) / (-this.Cc[zoom]); var lon = (px[0] - this.zc[zoom]) / this.Bc[zoom]; var lat = R2D * (2 * Math.atan(Math.exp(g)) - 0.5 * Math.PI); return [lon, lat]; }; // Convert tile xyz value to bbox of the form `[w, s, e, n]` // // - `x` {Number} x (longitude) number. // - `y` {Number} y (latitude) number. // - `zoom` {Number} zoom. // - `tms_style` {Boolean} whether to compute using tms-style. // - `srs` {String} projection for resulting bbox (WGS84|900913). // - `return` {Array} bbox array of values in form `[w, s, e, n]`. SphericalMercator.prototype.bbox = function(x, y, zoom, tms_style, srs) { // Convert xyz into bbox with srs WGS84 if (tms_style) { y = (Math.pow(2, zoom) - 1) - y; } // Use +y to make sure it's a number to avoid inadvertent concatenation. var ll = [x * this.size, (+y + 1) * this.size]; // lower left // Use +x to make sure it's a number to avoid inadvertent concatenation. var ur = [(+x + 1) * this.size, y * this.size]; // upper right var bbox = this.ll(ll, zoom).concat(this.ll(ur, zoom)); // If web mercator requested reproject to 900913. if (srs === '900913') { return this.convert(bbox, '900913'); } else { return bbox; } }; // Convert bbox to xyx bounds // // - `bbox` {Number} bbox in the form `[w, s, e, n]`. // - `zoom` {Number} zoom. // - `tms_style` {Boolean} whether to compute using tms-style. // - `srs` {String} projection of input bbox (WGS84|900913). // - `@return` {Object} XYZ bounds containing minX, maxX, minY, maxY properties. SphericalMercator.prototype.xyz = function(bbox, zoom, tms_style, srs) { // If web mercator provided reproject to WGS84. if (srs === '900913') { bbox = this.convert(bbox, 'WGS84'); } var ll = [bbox[0], bbox[1]]; // lower left var ur = [bbox[2], bbox[3]]; // upper right var px_ll = this.px(ll, zoom); var px_ur = this.px(ur, zoom); // Y = 0 for XYZ is the top hence minY uses px_ur[1]. var bounds = { minX: Math.floor(px_ll[0] / this.size), minY: Math.floor(px_ur[1] / this.size), maxX: Math.floor((px_ur[0] - 1) / this.size), maxY: Math.floor((px_ll[1] - 1) / this.size) }; if (tms_style) { var tms = { minY: (Math.pow(2, zoom) - 1) - bounds.maxY, maxY: (Math.pow(2, zoom) - 1) - bounds.minY }; bounds.minY = tms.minY; bounds.maxY = tms.maxY; } return bounds; }; // Convert projection of given bbox. // // - `bbox` {Number} bbox in the form `[w, s, e, n]`. // - `to` {String} projection of output bbox (WGS84|900913). Input bbox // assumed to be the "other" projection. // - `@return` {Object} bbox with reprojected coordinates. SphericalMercator.prototype.convert = function(bbox, to) { if (to === '900913') { return this.forward(bbox.slice(0, 2)).concat(this.forward(bbox.slice(2,4))); } else { return this.inverse(bbox.slice(0, 2)).concat(this.inverse(bbox.slice(2,4))); } }; // Convert lon/lat values to 900913 x/y. SphericalMercator.prototype.forward = function(ll) { var xy = [ A * ll[0] * D2R, A * Math.log(Math.tan((Math.PI*0.25) + (0.5 * ll[1] * D2R))) ]; // if xy value is beyond maxextent (e.g. poles), return maxextent. (xy[0] > MAXEXTENT) && (xy[0] = MAXEXTENT); (xy[0] < -MAXEXTENT) && (xy[0] = -MAXEXTENT); (xy[1] > MAXEXTENT) && (xy[1] = MAXEXTENT); (xy[1] < -MAXEXTENT) && (xy[1] = -MAXEXTENT); return xy; }; // Convert 900913 x/y values to lon/lat. SphericalMercator.prototype.inverse = function(xy) { return [ (xy[0] * R2D / A), ((Math.PI*0.5) - 2.0 * Math.atan(Math.exp(-xy[1] / A))) * R2D ]; }; return SphericalMercator; })(); if (typeof module !== 'undefined' && typeof exports !== 'undefined') { module.exports = exports = SphericalMercator; } },{}],7:[function(require,module,exports){ (function (root, factory) { if (typeof exports === 'object') { module.exports = factory(); } else if (typeof define === 'function' && define.amd) { define(factory); } else { root.animator = factory(); } }(this, function(){ "use strict"; var active = false, FRAME_RATE = 1000 / 60, TEST_PROPS = [ 'r', 'webkitR', 'mozR', 'oR', 'msR' ], callbacks = [], frameIndex = 0, useAnimFrame = typeof window != 'undefined' && (function() { for (var ii = 0; ii < TEST_PROPS.length; ii++) { window.animFrame = window.animFrame || window[TEST_PROPS[ii] + 'equestAnimationFrame']; } // for return animFrame; })(), BACK_S = 1.70158, HALF_PI = Math.PI / 2, TWO_PI = Math.PI * 2, ANI_WAIT = 1000 / 60 | 0, // initialise math function shortcuts abs = Math.abs, pow = Math.pow, sin = Math.sin, asin = Math.asin, cos = Math.cos, easingFns; function tick() { return (typeof performance != 'undefined' ? performance : Date).now() } function frame(tickCount) { var ii, cbData; // set the tick count in the case that it hasn't been set already // tickCount = tickCount || window.mozAnimationStartTime || Date.now(); // replace tickcount with date.now // TODO: replace with the correct timing helper tickCount = tickCount || tick(); // iterate through the callbacks for (ii = callbacks.length; ii--; ) { cbData = callbacks[ii]; // check to see if this callback should fire this frame if (frameIndex % cbData.every === 0) { cbData.cb(tickCount, frameIndex); } // if } // for // increment the frame index frameIndex++; // schedule the animator for another call if (useAnimFrame && active) { animFrame(frame); } // if } // frame function detach(callback) { var ii; // iterate through the callbacks and remove the specified one for (ii = callbacks.length; ii--; ) { if (callbacks[ii].cb === callback) { callbacks.splice(ii, 1); break; } // if } // for // if we have no callbacks remaining, deativate if (callbacks.length === 0) { if (! useAnimFrame) { clearInterval(active); } active = false; } } /** ## animator */ function animator(callback, every) { callbacks[callbacks.length] = { cb: callback, every: every ? Math.round(every / FRAME_RATE) : 1 }; if (! active) { // bind to the animframe callback active = (useAnimFrame ? animFrame(frame) : setInterval(frame, 1000 / 60)) || true; } // return a detach helper return { stop: detach.bind(null, callback) }; }; /** ## tween(duration, callback) */ animator.tween = function(callback, duration) { var startTicks = tick(), tween; // initialise the duration to 1000 if not set duration = duration || 1000; // start the tween tween = animator(function(tickCount) { // calculate the updated value var elapsed = (tickCount || tick()) - startTicks, complete = elapsed >= duration, ret; ret = callback(elapsed, duration, complete); if (complete || (typeof ret != 'undefined' && (! ret))) { tween.stop(); } }); }; /* # Easing functions sourced from Robert Penner's excellent work: http://www.robertpenner.com/easing/ Functions follow the function format of fn(t, b, c, d, s) where: - t = time - b = beginning position - c = change - d = duration */ easingFns = animator.easing = { linear: function(t, b, c, d) { return c*t/d + b; }, /* back easing functions */ backin: function(t, b, c, d) { return c*(t/=d)*t*((BACK_S+1)*t - BACK_S) + b; }, backout: function(t, b, c, d) { return c*((t=t/d-1)*t*((BACK_S+1)*t + BACK_S) + 1) + b; }, /* backinout: function(t, b, c, d) { return ((t/=d/2)<1) ? c/2*(t*t*(((BACK_S*=(1.525))+1)*t-BACK_S))+b : c/2*((t-=2)*t*(((BACK_S*=(1.525))+1)*t+BACK_S)+2)+b; }, */ /* bounce easing functions */ bouncein: function(t, b, c, d) { return c - easingFns.bounceout(d-t, 0, c, d) + b; }, bounceout: function(t, b, c, d) { if ((t/=d) < (1/2.75)) { return c*(7.5625*t*t) + b; } else if (t < (2/2.75)) { return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b; } else if (t < (2.5/2.75)) { return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b; } else { return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b; } }, bounceinout: function(t, b, c, d) { if (t < d/2) return easingFns.bouncein(t*2, 0, c, d) / 2 + b; else return easingFns.bounceout(t*2-d, 0, c, d) / 2 + c/2 + b; }, /* cubic easing functions */ cubicin: function(t, b, c, d) { return c*(t/=d)*t*t + b; }, cubicout: function(t, b, c, d) { return c*((t=t/d-1)*t*t + 1) + b; }, cubicinout: function(t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t + b; return c/2*((t-=2)*t*t + 2) + b; }, /* elastic easing functions */ elasticin: function(t, b, c, d, a, p) { var s; if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*0.3; if (!a || a < abs(c)) { a=c; s=p/4; } else s = p/TWO_PI * asin (c/a); return -(a*pow(2,10*(t-=1)) * sin( (t*d-s)*TWO_PI/p )) + b; }, elasticout: function(t, b, c, d, a, p) { var s; if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*0.3; if (!a || a < abs(c)) { a=c; s=p/4; } else s = p/TWO_PI * asin (c/a); return (a*pow(2,-10*t) * sin( (t*d-s)*TWO_PI/p ) + c + b); }, elasticinout: function(t, b, c, d, a, p) { var s; if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(0.3*1.5); if (!a || a < abs(c)) { a=c; s=p/4; } else s = p/TWO_PI * asin (c/a); if (t < 1) return -0.5*(a*pow(2,10*(t-=1)) * sin( (t*d-s)*TWO_PI/p )) + b; return a*pow(2,-10*(t-=1)) * sin( (t*d-s)*TWO_PI/p )*0.5 + c + b; }, /* quad easing */ quadin: function(t, b, c, d) { return c*(t/=d)*t + b; }, quadout: function(t, b, c, d) { return -c *(t/=d)*(t-2) + b; }, quadinout: function(t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t + b; return -c/2 * ((--t)*(t-2) - 1) + b; }, /* sine easing */ sinein: function(t, b, c, d) { return -c * cos(t/d * HALF_PI) + c + b; }, sineout: function(t, b, c, d) { return c * sin(t/d * HALF_PI) + b; }, sineinout: function(t, b, c, d) { return -c/2 * (cos(Math.PI*t/d) - 1) + b; } }; return animator; })); },{}]},{},[1]) ;