block by RandomEtc 599724

Modest Maps JS - Smooth Efficient Zooming and Panning

Full Screen

index.html

<html>
<head>
<title>Modest Maps JS - Smooth Efficient Zooming and Panning</title>
<script type="text/javascript" src="//github.com/stamen/modestmaps-js/raw/master/modestmaps.js"></script>
<script type="text/javascript">

var map;

function initMap() {

    var container = document.getElementById('container');

    var template = '//{S}tile.openstreetmap.org/{Z}/{X}/{Y}.png';
    var subdomains = [ 'a.', 'b.', 'c.', ''];
    var provider = new com.modestmaps.TemplatedMapProvider(template, subdomains);

    map = new com.modestmaps.Map('map', provider); 

    map.setCenterZoom(new com.modestmaps.Location(37.811530, -122.2666097), 14);

    animateCenterZoom(new com.modestmaps.Location(51.514, -0.113), 18);
}

function animateCenterZoom(l1, z1) {

    var start = map.provider.locationCoordinate(map.getCenter()).zoomTo(0),
        end   = map.provider.locationCoordinate(l1).zoomTo(0);
        
    var c0 = { x: start.column, y: start.row },
        c1 = { x: end.column, y: end.row };

    // how much world can we see at zoom 0?
    var topLeft = map.pointCoordinate(new com.modestmaps.Point(0,0)).zoomTo(0);
    var bottomRight = map.pointCoordinate(map.dimensions).zoomTo(0);
    var w0 = Math.max(bottomRight.column-topLeft.column, bottomRight.row-topLeft.row);

    // z1 is ds times bigger than this zoom:
    var ds = Math.pow(2, z1 - map.getZoom());
    
    // so how much world at zoom z1?
    var w1 = w0 / ds;

    // GO!
    animateStep(c0, w0, c1, w1);

}

/*

    From "Smooth and efficient zooming and panning"
    by Jarke J. van Wijk and Wim A.A. Nuij

    You only need to understand section 3 (equations 1 through 5) 
    and then you can skip to equation 9, implemented below:

*/

function sq(n) { return n*n; }
function dist(a,b) { return Math.sqrt(sq(b.x-a.x)+sq(b.y-a.y)); }
function lerp1(a,b,p) { return a + ((b-a) * p) }
function lerp2(a,b,p) { return { x: lerp1(a.x,b.x,p), y: lerp1(a.y,b.y,p) }; }
function cosh(x) { return (Math.pow(Math.E,x) + Math.pow(Math.E,-x)) / 2; }
function sinh(x) { return (Math.pow(Math.E,x) - Math.pow(Math.E,-x)) / 2; }
function tanh(x) { return sinh(x) / cosh(x); }

function animateStep(c0,w0,c1,w1,V,rho) {

    // see section 6 for user testing to derive these values (they can be tuned)
    if (V === undefined) V = 0.9;
    if (rho === undefined) rho = 1.42

    // simple interpolation of positions will be fine:
    var u0 = 0,
        u1 = dist(c0,c1);

    // i = 0 or 1
    function b(i) {
        var n = sq(w1) - sq(w0) + ((i ? -1 : 1) * Math.pow(rho,4) * sq(u1-u0));
        var d = 2 * (i ? w1 : w0) * sq(rho) * (u1-u0);
        return n / d;
    }
    
    // give this a b(0) or b(1)
    function r(b) {
        return Math.log(-b + Math.sqrt(sq(b)+1));
    }
    
    var r0 = r(b(0)),
        r1 = r(b(1)),
        S = (r1-r0) / rho; // "distance"
    
    function u(s) {
        var a = w0/sq(rho),
            b = a * cosh(r0) * tanh(rho*s + r0),
            c = a * sinh(r0);
        return b - c + u0;
    }
    
    function w(s) {
        return w0 * cosh(r0) / cosh(rho*s + r0);
    }

    var t0 = Date.now();
    var interval = setInterval(function() {
        var t1 = Date.now();
        var t = (t1 - t0) / 1000.0;
        var s = V * t;
        if (s > S) {
            s = S;
            clearInterval(interval);
        }
        var us = u(s);
        var pos = lerp2(c0,c1,(us-u0)/(u1-u0));
        applyPos(pos, w(s));
    }, 40);

}

function applyPos(pos,w) {
    var c = new com.modestmaps.Coordinate(pos.y,pos.x,0);
    var l = map.provider.coordinateLocation(c);

    // how much world can we see at zoom 0?
    var topLeft = map.pointCoordinate(new com.modestmaps.Point(0,0)).zoomTo(0);
    var bottomRight = map.pointCoordinate(map.dimensions).zoomTo(0);
    var w0 = Math.max(bottomRight.column-topLeft.column, bottomRight.row-topLeft.row);    

    // so what's our new zoom?
    var z = map.getZoom() + (Math.log(w0/w) / Math.LN2)
    map.setCenterZoom(l,z);    
}

</script>
<style type="text/css">
body {
  margin: 0;
  padding: 0;
  border: 0
}
#map {
  width: 100%;
  height: 100%;
}
</style>
</head>
<body onload="initMap()">
  <div id="map">
  </div>
</body>
</html>

README

http://www.win.tue.nl/~vanwijk/zoompan.pdf for Modest Maps JS