Skip to content

Instantly share code, notes, and snippets.

@RandomEtc
Created September 27, 2010 20:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RandomEtc/599724 to your computer and use it in GitHub Desktop.
Save RandomEtc/599724 to your computer and use it in GitHub Desktop.
Modest Maps JS - Smooth Efficient Zooming and Panning
http://www.win.tue.nl/~vanwijk/zoompan.pdf for Modest Maps JS
<html>
<head>
<title>Modest Maps JS - Smooth Efficient Zooming and Panning</title>
<script type="text/javascript" src="http://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 = 'http://{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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment