11 metros
Boston Philadelphia
● ●
Charlotte San Francisco
● ●
Chicago San Jose
● ●
Los Angeles Seattle
● ●
Miami Washington, DC
● ●
New York … and growing
●
TileCache
pro con
Cache population Python overhead
● ●
integrated with (rendering, serving)
request/response cycle
Flexible storage
●
Pre-render + custom nginx mod
pro con
Fast responses Render everything in
● ●
advance
Parallelizable, offline
●
rendering C module inflexibility
●
(esp. storage backends)
// ngx_tilecache_mod.c
/*
* This struct holds the attributes that uniquely identify a map tile.
*/
typedef struct {
u_char *version;
u_char *name;
int x;
int y;
int z;
u_char *ext;
} tilecache_tile_t;
/*
* The following regex pattern matches the request URI for a tile and
* creates capture groups for the tile attributes. Example request URI:
*
* /1.0/main/8/654,23.png
*
* would map to the following attributes:
*
* version: 1.0
* name: main
* z: 8
* x: 654
* y: 23
* extension: png
*/
static ngx_str_t tile_request_pat = ngx_string(quot;^/([^/]+)/([^/]+)/([0-9]+)/([0-9]+),([0-9]+).([a-z]+)$quot;);
// ngx_tilecache_mod.c
u_char *
get_disk_key(u_char *s, u_char *name, int x, int y, int z, u_char *ext)
{
u_int a, b, c, d, e, f;
a = x / 100000;
b = (x / 1000) % 1000;
c = x % 1000;
d = y / 100000;
e = (y / 1000) % 1000;
f = y % 1000;
return ngx_sprintf(s, quot;/%s/%02d/%03d/%03d/%03d/%03d/%03d/%03d.%squot;,
name, z, a, b, c, d, e, f, ext);
}
static ngx_int_t
ngx_tilecache_handler(ngx_http_request_t *r)
{
// ... snip ...
sub_uri.data = ngx_pcalloc(r->pool, len + 1);
if (sub_uri.data == NULL) {
return NGX_ERROR;
}
get_disk_key(sub_uri.data, tile->name, tile->x, tile->y, tile->z, tile->ext);
sub_uri.len = ngx_strlen(sub_uri.data);
return ngx_http_internal_redirect(r, &sub_uri, &r->args);
}
# cluster.py
import math
from everyblock.maps.clustering.models import Bunch
def euclidean_distance(a, b):
return math.hypot(a[0] - b[0], a[1] - b[1])
def buffer_cluster(objects, radius, dist_fn=euclidean_distance):
bunches = []
buffer_ = radius
for key, point in objects.iteritems():
bunched = False
for bunch in bunches:
if dist_fn(point, bunch.center) <= buffer_:
bunch.add_obj(key, point)
bunched = True
break
if not bunched:
bunches.append(Bunch(key, point))
return bunches
# bunch.py
class Bunch(object):
def __init__(self, obj, point):
self.objects = []
self.points = []
self.center = (0, 0)
self.add_obj(obj, point)
def add_obj(self, obj, point):
self.objects.append(obj)
self.points.append(point)
self.update_center(point)
def update_center(self, point):
xs = [p[0] for p in self.points]
ys = [p[1] for p in self.points]
self.center = (sum(xs) * 1.0 / len(self.objects),
sum(ys) * 1.0 / len(self.objects))
# cluster_scale.py
from everyblock.maps import utils
from everyblock.maps.clustering import cluster
def cluster_by_scale(objs, radius, scale, extent=(-180, -90, 180, 90)):
resolution = utils.get_resolution(scale)
# Translate from lng/lat into coordinate system of the display.
objs = dict([(k, utils.px_from_lnglat(v, resolution, extent))
for k, v in objs.iteritems()])
bunches = []
for bunch in cluster.buffer_cluster(objs, radius):
# Translate back into lng/lat.
bunch.center = utils.lnglat_from_px(bunch.center,
resolution,
extent)
bunches.append(bunch)
return bunches