Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active February 8, 2016 23:52
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save mbostock/900050 to your computer and use it in GitHub Desktop.
Save mbostock/900050 to your computer and use it in GitHub Desktop.
d3.geo.tiler
license: gpl-3.0
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://cdn.rawgit.com/mbostock/d3/v1.8.4/d3.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/mbostock/d3/v1.8.4/d3.geo.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/simplegeo/polymaps/v2.4.0/polymaps.js"></script>
<script type="text/javascript" src="tiler.js"></script>
<style type="text/css">
@import url("https://cdn.rawgit.com/simplegeo/polymaps/v2.4.0/examples/example.css");
html, body {
height: 100%;
background: #E6E6E6;
margin: 0;
font: 10px sans-serif;
}
svg {
display: block;
}
circle {
stroke: black;
fill: brown;
fill-opacity: .5;
}
#map {
width: 960px;
height: 500px;
}
</style>
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
var po = org.polymaps;
// Create the map object, add it to #map…
var map = po.map()
.container(d3.select("#map").append("svg:svg").node())
.zoom(8)
.add(po.drag())
.add(po.wheel().smooth(false))
.add(po.dblclick())
.add(po.arrow());
// Add the CloudMade image tiles as a base layer…
map.add(po.image()
.url(po.url("http://{S}tile.cloudmade.com"
+ "/1a1b06b230af4efdbb989ea99e9841af" // http://cloudmade.com/register
+ "/998/256/{Z}/{X}/{Y}.png")
.hosts(["a.", "b.", "c.", ""])));
// Add the custom stations layer…
map.add(stations("stations.json"));
// Add the compass control on top.
map.add(po.compass()
.pan("none"));
// Custom layer implementation.
function stations(url) {
// Create the tiler, for organizing our points into tile boundaries.
var tiler = d3.geo.tiler()
.zoom(11)
.location(function(d) { return d.value; });
// Create the base layer object, using our tile factory.
var layer = po.layer(load);
// Load the station data. When the data comes back, reload.
d3.json(url, function(json) {
tiler.points(d3.entries(json));
layer.reload();
});
// Custom tile implementation.
function load(tile, projection) {
projection = projection(tile).locationPoint;
// Add an svg:g for each station.
var g = d3.select(tile.element = po.svg("g")).selectAll("g")
.data(tiler.tile(tile.column, tile.row, tile.zoom))
.enter().append("svg:g")
.attr("transform", transform);
// Add a circle.
g.append("svg:circle")
.style("fill", d3.hsl(Math.random() * 360, 1, .5))
.attr("r", 4.5);
// Add a label.
g.append("svg:text")
.attr("x", 7)
.attr("dy", ".31em")
.text(function(d) { return d.key; });
function transform(d) {
d = projection({lon: d.value[0], lat: d.value[1]});
return "translate(" + d.x + "," + d.y + ")";
}
}
return layer;
}
</script>
</body>
</html>
{"KMAE":[-120.12,36.98,"MADERA MUNICIPAL AIRPORT",[26,1,2,5,6,3,2,1,2,7,29,12,3]],"KSJC":[-121.92,37.37,"SAN JOSE INTERNATIONAL AIRPORT",[28,1,1,1,6,10,5,3,2,4,14,21,7]],"KMCE":[-120.50,37.28,"MERCED MUNICIPAL AIRPORT",[29,1,1,3,7,5,2,1,3,6,12,26,5]],"KMER":[-120.57,37.37,"Merced / Castle Air Force Base",[34,1,1,1,4,5,2,1,1,4,17,22,7]],"KAPC":[-122.28,38.20,"NAPA COUNTY AIRPORT",[23,2,1,6,3,3,8,18,11,13,4,3,5]],"KSUU":[-121.95,38.27,"Fairfield / Travis Air Force Base",[13,7,4,3,3,6,4,13,33,4,1,2,7]],"KSQL":[-122.25,37.52,"San Carlos Airport",[18,3,2,2,3,4,3,2,5,17,16,12,12]],"KSNS":[-121.60,36.67,"SALINAS MUNICIPAL AIRPORT",[21,1,1,6,12,3,1,2,9,21,17,5,1]],"KMOD":[-120.95,37.62,"MODESTO CITY CO SHAM FLD",[27,1,1,2,10,5,1,1,1,3,17,24,8]],"KOAK":[-122.23,37.72,"METRO OAKLAND INTERNATIONAL AIRPORT ",[16,3,3,2,4,6,3,4,9,23,20,6,2]],"KSCK":[-121.23,37.90,"STOCKTON METROPOLITAN AIRPORT ",[21,2,2,3,6,8,2,1,4,15,19,12,4]],"KCCR":[-122.05,38.00,"CONCORD BUCHANAN FIELD",[24,3,2,1,1,5,17,12,9,9,7,6,4]],"KMRY":[-121.85,36.58,"MONTEREY PENINSULA AIRPORT",[26,1,2,9,5,3,4,9,13,14,9,4,1]],"KPAO":[-122.12,37.47,"Palo Alto Airport",[31,3,1,1,2,5,1,1,1,4,10,25,14]],"KSAC":[-121.50,38.50,"SACRAMENTO EXECUTIVE AIRPORT ",[32,1,0,1,3,11,12,16,5,2,4,9,3]],"KHWD":[-122.12,37.67,"HAYWARD AIR TERMINAL",[20,2,7,2,2,6,3,3,6,23,18,6,2]],"KSTS":[-122.82,38.50,"SANTA ROSA SONOMA COUNTY",[46,1,0,1,5,13,10,4,3,3,4,6,3]],"KSMF":[-121.60,38.70,"SACRAMENTO INTERNATIONAL AIRPORT",[19,2,1,2,4,21,18,8,3,2,5,12,4]],"KNUQ":[-122.05,37.43,"MOFFETT FIELD",[35,3,1,1,4,7,2,1,2,5,6,17,15]],"KRHV":[-121.82,37.33,"San Jose / Reid / Hillv",[35,0,0,1,4,4,2,1,1,10,28,11,1]],"KWVI":[-121.78,36.93,"WATSONVILLE MUNICIPAL AIRPORT ",[44,1,2,3,4,5,7,9,8,4,6,5,2]],"KMHR":[-121.30,38.55,"Sacramento, Sacramento Mather Airport",[21,1,1,2,8,15,12,12,7,4,5,7,3]],"KVCB":[-121.95,38.38,"VACAVILLE NUT TREE AIRPORT",[36,2,1,1,2,6,10,18,10,2,2,5,6]],"KSFO":[-122.37,37.62,"SAN FRANCISCO INTERNATIONAL AIRPORT ",[13,3,3,2,3,4,4,4,7,31,20,2,3]],"KLVK":[-121.82,37.70,"LIVERMORE MUNICIPAL AIRPORT ",[32,2,7,3,1,1,2,7,9,17,16,2,1]]}
d3.geo.tiler = function() {
var tiler = {},
points = [],
projection = d3.geo.mercator().scale(1).translate([.5, .5]),
location = Object, // identity function
zoom = 8,
root = null;
function build(points, x, y, z) {
if (z >= zoom) return points.map(d3_geo_tilerData);
var i = -1,
n = points.length,
c = [[], [], [], []],
k = 1 << z++,
p;
while (++i < n) {
p = points[i];
var x1 = (p[0] * k - x) >= .5,
y1 = (p[1] * k - y) >= .5;
c[x1 << 1 | y1].push(p);
}
x <<= 1;
y <<= 1;
return {
"0": c[0].length && build(c[0], x , y , z),
"1": c[1].length && build(c[1], x , y + 1, z),
"2": c[2].length && build(c[2], x + 1, y , z),
"3": c[3].length && build(c[3], x + 1, y + 1, z)
};
}
tiler.location = function(x) {
if (!arguments.length) return location;
location = x;
root = null; // reset
return tiler;
};
tiler.projection = function(x) {
if (!arguments.length) return projection;
projection = x;
root = null; // reset
return tiler;
};
tiler.zoom = function(x) {
if (!arguments.length) return zoom;
zoom = x;
root = null; // reset
return tiler;
};
tiler.points = function(x) {
if (!arguments.length) return points;
points = x;
root = null; // reset
return tiler;
};
tiler.tile = function(x, y, z) {
var results = [];
// Lazy initialization…
// Project the points to normalized coordinates in [0, 1].
if (!root) {
root = build(points.map(function(d, i) {
var point = projection(location.call(tiler, d, i));
point.data = d;
return point;
}), 0, 0, 0);
}
function search(node, x0, y0, z0) {
if (!node) return;
if (z0 < z) {
var k = Math.pow(2, z0 - z),
x1 = (x * k - x0) >= .5,
y1 = (y * k - y0) >= .5;
search(node[x1 << 1 | y1], x0 << 1 | x1, y0 << 1 | y1, z0 + 1);
} else {
accumulate(node);
}
}
function accumulate(node) {
if (node.length) {
for (var i = -1, n = node.length; ++i < n;) {
results.push(node[i]);
}
} else {
for (var i = -1; ++i < 4;) {
if (node[i]) accumulate(node[i]);
}
}
}
search(root, 0, 0, 0);
return results;
};
return tiler;
};
function d3_geo_tilerData(d) {
return d.data;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment