Skip to content

Instantly share code, notes, and snippets.

@trevorgerhardt
Created June 11, 2014 01:07
Show Gist options
  • Save trevorgerhardt/69286b523d00d0d8c3d6 to your computer and use it in GitHub Desktop.
Save trevorgerhardt/69286b523d00d0d8c3d6 to your computer and use it in GitHub Desktop.
Map tile manual zooming on lat/lon bounds

Experiment to zoom in and out based on given lat/lon bounds. Projects and translates the bounds to figure out the new scale/translation vector to pass to the geo.tile function. Was more annoying than I thought it would be.

<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<style>
body {
margin: 0;
font-family: 'Helvetica', arial, sans-serif;
font-weight: 300;
width: 500px;
height: 500px;
}
.map {
position: relative;
overflow: hidden;
}
.layer {
position: absolute;
}
.tile {
pointer-events: none;
position: absolute;
width: 256px;
height: 256px;
}
form {
position: absolute;
z-index: 100;
border-radius: 10px;
margin: 10px;
}
input {
font-size: 1em;
padding: 5px;
font-weight: 300;
}
input[type=submit] {
background-color: #fff;
text-align: center;
}
form span {
display: inline-block;
padding: 5px;
}
</style>
</head>
<body>
<form>
<input type="text" name="lat1" value="39">
<input type="text" name="lon1" value="-79">
<br>
<input type="text" name="lat2" value="37">
<input type="text" name="lon2" value="-77">
<br>
<span>x:</span><span id="translatex"></span><span>y:</span><span id="translatey"></span><span>scale:</span> <span id="scale"></span>
<br>
<input type="submit" value="Update">
</form>
<script src='http://d3js.org/d3.v3.min.js'></script>
<script src='http://d3js.org/d3.geo.tile.v0.min.js'></script>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var prefix = prefixMatch(['webkit', 'ms', 'Moz', 'O']);
var input = {
lat1: d3.select('[name=lat1]'),
lon1: d3.select('[name=lon1]'),
lat2: d3.select('[name=lat2]'),
lon2: d3.select('[name=lon2]'),
translatex: d3.select('#translatex'),
translatey: d3.select('#translatey'),
scale: d3.select('#scale')
};
var tile = d3.geo.tile()
.size([width, height]);
var zoom = d3.behavior.zoom()
.scale(1 << 12)
.scaleExtent([1 << 10, 1 << 23])
.translate([width / 2, height / 2])
.on('zoom', zoomed);
var projection = d3.geo.mercator()
.scale(zoom.scale() / 2 / Math.PI)
.translate([width / 2, height / 2]);
var map = d3.select('body').append('div')
.attr('class', 'map')
.style('width', width + 'px')
.style('height', height + 'px')
.call(zoom);
var layer = map.append('div')
.attr('class', 'layer');
document.getElementsByTagName('form')[0].addEventListener('submit', function(e) {
e.preventDefault();
submit();
});
zoomed();
function zoomed(is, it) {
var s = is || zoom.scale();
var t = it || zoom.translate();
tile
.scale(s)
.translate(t);
projection
.scale(s / 2 / Math.PI)
.translate(t);
var nw = projection.invert([ 0, 0 ]);
var se = projection.invert([ width, height ]);
input.lat1.property('value', nw[1]);
input.lon1.property('value', nw[0]);
input.lat2.property('value', se[1]);
input.lon2.property('value', se[0]);
var r = Math.round;
input.scale.text(r(s));
input.translatex.text(r(t[0]));
input.translatey.text(r(t[1]));
renderTiles(tile());
}
function renderTiles(tiles) {
var image = layer
.style(prefix + 'transform', matrix3d(tiles.scale, tiles.translate))
.selectAll('.tile')
.data(tiles, function(d) {
return d;
});
image.exit()
.remove();
image.enter().append('img')
.attr('class', 'tile')
.attr('src', function(d) {
return 'http://' + ['a', 'b', 'c', 'd'][Math.random() * 4 | 0] +
'.tiles.mapbox.com/v3/conveyal.ie3o67m0/' + d[2] + '/' + d[0] +
'/' + d[1] + '.png';
})
.style('left', function(d) {
return (d[0] << 8) + 'px';
})
.style('top', function(d) {
return (d[1] << 8) + 'px';
});
}
function submit() {
var f = parseFloat;
zoomToLLBounds([f(input.lon1.property('value')), f(input.lat1.property('value'))], [f(input.lon2.property('value')), f(input.lat2.property('value'))]);
}
function zoomToLLBounds(nw, se) {
var pnw = projection(nw);
var pse = projection(se);
var scale = zoom.scale();
var translate = zoom.translate();
var dx = pse[0] - pnw[0];
var dy = pse[1] - pnw[1];
scale = scale * (1 / Math.max(dx / width, dy / height));
projection
.translate([ width / 2, height / 2])
.scale(scale / 2 / Math.PI);
// reproject
pnw = projection(nw);
pse = projection(se);
var x = (pnw[0] + pse[0]) / 2;
var y = (pnw[1] + pse[1]) / 2;
translate = [width - x, height - y];
zoom
.scale(scale)
.translate(translate);
zoomed(scale, translate);
}
function matrix3d(scale, translate) {
var k = scale / 256,
r = scale % 1 ? Number : Math.round;
return 'matrix3d(' + [k, 0, 0, 0, 0, k, 0, 0, 0, 0, k, 0, r(translate[0] *
scale), r(translate[1] * scale), 0, 1] + ')';
}
function prefixMatch(p) {
var i = -1,
n = p.length,
s = document.body.style;
while (++i < n)
if (p[i] + 'Transform' in s) return '-' + p[i].toLowerCase() + '-';
return '';
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment