SlideShare a Scribd company logo
1 of 56
Download to read offline
The Web map stack on Django

                 Paul Smith
 http://www.pauladamsmith.com/ @paulsmith
                 EveryBlock
             EuroDjangoCon ‘09
Data types
11 metros
    Boston                 Philadelphia
●                      ●


    Charlotte              San Francisco
●                      ●


    Chicago                San Jose
●                      ●


    Los Angeles            Seattle
●                      ●


    Miami                  Washington, DC
●                      ●


    New York               … and growing
●
Open source
 This summer
Why?
Control design
Prioritize visualizations
The Web map stack
The Web map stack
The Web map stack
The Web map stack
The Web map stack
GeoDjango + Mapnik
   example app
“Your Political Footprint”
Mapnik overview
# models.py

from django.contrib.gis.db import models

class CongressionalDistrict(models.Model):
    state = models.ForeignKey(State)
    name = models.CharField(max_length=32) # ex. 1st, 25th, at-large
    number = models.IntegerField() # 0 if at-large
    district = models.MultiPolygonField(srid=4326)
    objects = models.GeoManager()

   def __unicode__(self):
       return '%s %s' % (self.state.name, self.name)

class Footprint(models.Model):
    location = models.CharField(max_length=200)
    point = models.PointField(srid=4326)
    cong_dist = models.ForeignKey(CongressionalDistrict)
    objects = models.GeoManager()

   def __unicode__(self):
       return '%s in %s' % (self.location, self.cong_dist)
GET /tile/?bbox=-112.5,22.5,-90,45
# urls.py

from django.conf import settings
from django.conf.urls.defaults import *
from edc_demo.footprint import views

urlpatterns = patterns('',
    (r'^footprint/', views.political_footprint),
    (r'^tile/', views.map_tile)
)
# views.py

from   mapnik import *
from   django.http import HttpResponse, Http404
from   django.conf import settings
from   edc_demo.footprint.models import CongressionalDistrict

TILE_WIDTH = TILE_HEIGHT = 256
TILE_MIMETYPE = 'image/png'
LIGHT_GREY = '#C0CCC4'
PGIS_DB_CONN = dict(
    host=settings.DATABASE_HOST,
    dbname=settings.DATABASE_NAME,
    user=settings.DATABASE_USER,
    password=settings.DATABASE_PASSWORD)

def map_tile(request):
    if request.GET.has_key('bbox'):
        bbox = [float(x) for x in request.GET['bbox'].split(',')]
        tile = Map(TILE_WIDTH, TILE_HEIGHT)
        rule = Rule()
        rule.symbols.append(LineSymbolizer(Color(LIGHT_GREY), 1.0))
        style = Style()
        style.rules.append(rule)
        tile.append_style('cong_dist', style)
        layer = Layer('cong_dists')
        db_table = CongressionalDistrict._meta.db_table
        layer.datasource = PostGIS(table=db_table, **PGIS_DB_CONN)
        layer.styles.append('cong_dist')
        tile.layers.append(layer)
        tile.zoom_to_box(Envelope(*bbox))
        img = Image(tile.width, tile.height)
        render(tile, img)
        img_bytes = img.tostring(TILE_MIMETYPE.split('/')[1])
        return HttpResponse(img_bytes, mimetype=TILE_MIMETYPE)
    else:
        raise Http404()
# views.py cont'd

from django.shortcuts import render_to_response
from edc_demo.footprint.geocoder import geocode

def political_footprint(request):
    context = {}
    if request.GET.has_key('location'):
        point = geocode(request.GET['location'])
        cd = CongressionalDistrict.objects.get(district__contains=point)
        footprint = Footprint.objects.create(
            location = request.GET['location'],
            point = point,
            cong_dist = cd
        )
        context['footprint'] = footprint
        context['cd_bbox'] = cong_dist.district.extent
    return render_to_response('footprint.html', context)
// footprint.html
<script type=quot;text/javascriptquot;>
var map;
var TileLayerClass = OpenLayers.Class(OpenLayers.Layer.TMS, {
    initialize: function(footprint_id) {
         var name = quot;tilesquot;;
         var url = quot;http://127.0.0.1:8000/tile/quot;;
         var args = [];
         args.push(name, url, {}, {});
         OpenLayers.Layer.Grid.prototype.initialize.apply(this, args);
         this.footprint_id = footprint_id;
    },

    getURL: function(bounds) {
         var url = this.url + quot;?bbox=quot; + bounds.toBBOX();
         if (this.footprint_id)
              url += quot;&fp_id=quot; + this.footprint_id;
         return url;
     }
});
function onload() {
    var options = {
          minScale: 19660800,
          numZoomLevels: 14,
          units: quot;degreesquot;
    };
    map = new OpenLayers.Map(quot;mapquot;);
    {% if not footprint %}
     var bbox = new OpenLayers.Bounds(-126.298828, 17.578125, -64.775391, 57.128906);
     var tileLayer = new TileLayerClass();
    {% else %}
     var bbox = new OpenLayers.Bounds({{ cd_bbox|join:quot;, quot; }});
     var tileLayer = new TileLayerClass({{ footprint.id }});
    {% endif %}
    map.addLayer(tileLayer);
    map.zoomToExtent(bbox);
}
# views.py

from edc_demo.footprint.models import Footprint

def map_tile(request):
    if request.GET.has_key('bbox'):
        bbox = [float(x) for x in request.GET['bbox'].split(',')]
        tile = Map(TILE_WIDTH, TILE_HEIGHT)
        rule = Rule()
        rule.symbols.append(LineSymbolizer(Color(LIGHT_GREY), 1.0))
        style = Style()
        style.rules.append(rule)
        if request.GET.has_key('fp_id'):
            footprint = Footprint.objects.get(pk=request.GET['fp_id'])
            rule = Rule()
            rule.symbols.append(LineSymbolizer(Color(GREEN), 1.0))
            rule.symbols.append(PolygonSymbolizer(Color(LIGHT_GREEN)))
            rule.filter = Filter('[id] = ' + str(footprint.cong_dist.id))
            style.rules.append(rule)
        tile.append_style('cong_dist', style)
        layer = Layer('cong_dists')
        db_table = CongressionalDistrict._meta.db_table
        layer.datasource = PostGIS(table=db_table, **PGIS_DB_CONN)
        layer.styles.append('cong_dist')
        tile.layers.append(layer)
        if request.GET.has_key('fp_id'):
            add_footprint_layer(tile, footprint)
        tile.zoom_to_box(Envelope(*bbox))
        img = Image(tile.width, tile.height)
        render(tile, img)
        img_bytes = img.tostring(TILE_MIMETYPE.split('/')[1])
        return HttpResponse(img_bytes, mimetype=TILE_MIMETYPE)
    else:
        raise Http404()
# views.py cont'd

def add_footprint_layer(tile, footprint):
    rule = Rule()
    rule.symbols.append(
       PointSymbolizer(
           os.path.join(settings.STATIC_MEDIA_DIR, 'img', 'footprint.png'),
           'png', 46, 46)
    )
    rule.filter = Filter('[id] = ' + str(footprint.id))
    style = Style()
    style.rules.append(rule)
    tile.append_style('footprint', style)
    layer = Layer('footprint')
    layer.datasource = PostGIS(table=Footprint._meta.db_table, **PGIS_DB_CONN)
    layer.styles.append('footprint')
    tile.layers.append(layer)
Serving tiles
Zoom levels
Tile example




z: 5, x: 2384, y: 1352
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)
Tile rendering
for each zoom level z:
   for each column x:
      for each row y:
         render tile (x, y, z)
Tile rendering
for each zoom level z:
   for each column x:
      for each row y:
         render tile (x, y, z)
Tile rendering
for each zoom level z:
   for each column x:
      for each row y:
         render tile (x, y, z)
Tile rendering
for each zoom level z:
   for each column x:
      for each row y:
         render tile (x, y, z)
# nginx.conf

server {
    server_name     tile.example.com
    root            /var/www/maptiles;
    expires         max;
    location ~* ^/[^/]+/w+/d+/d+,d+.(jpg|gif|png)$ {
        tilecache;
    }
}
// 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);
}
Custom tile cache
    technique                 responsibility
    Far-future expiry         Tile versions for cache
●                         ●

    header expires max;       invalidation
// everyblock.js

eb.TileLayer = OpenLayers.Class(OpenLayers.Layer.TMS, {
    version: null, // see eb.TILE_VERSION
    layername: null, // lower-cased: quot;mainquot;, quot;locatorquot;
    type: null, // i.e., mime-type extension: quot;pngquot;, quot;jpgquot;, quot;gifquot;

      initialize: function(name, url, options) {
            var args = [];
            args.push(name, url, {}, options);
            OpenLayers.Layer.TMS.prototype.initialize.apply(this, args);
      },

      // Returns an object with the x, y, and z of a tile for a given bounds
      getCoordinate: function(bounds) {
            bounds = this.adjustBounds(bounds);
            var res = this.map.getResolution();
            var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));
            var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h));
            var z = this.map.getZoom();
            return {x: x, y: y, z: z};
      },

      getPath: function(x, y, z) {
            return this.version + quot;/quot; + this.layername + quot;/quot; + z + quot;/quot; + x + quot;,quot; + y + quot;.quot; +
                      this.type;
      },

      getURL: function(bounds) {
            var coord = this.getCoordinate(bounds);
            var path = this.getPath(coord.x, coord.y, coord.z);
            var url = this.url;
            if (url instanceof Array)
                 url = this.selectUrl(path, url);
            return url + path;
      },

      CLASS_NAME: quot;eb.TileLayerquot;
});
Clustering
# 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
Sneak peek
Sneak peek
Sneak peek
Thank you

http://www.pauladamsmith.com/
           @paulsmith
      paulsmith@gmail.com



     Further exploration:
    “How to Lie with Maps”
      Mark Monmonier

More Related Content

What's hot

Introduction to HTML5 Canvas
Introduction to HTML5 CanvasIntroduction to HTML5 Canvas
Introduction to HTML5 CanvasMindy McAdams
 
Grails : Ordr, Maps & Charts
Grails : Ordr, Maps & ChartsGrails : Ordr, Maps & Charts
Grails : Ordr, Maps & ChartsHenk Jurriens
 
Exploring Canvas
Exploring CanvasExploring Canvas
Exploring CanvasKevin Hoyt
 
Having fun with graphs, a short introduction to D3.js
Having fun with graphs, a short introduction to D3.jsHaving fun with graphs, a short introduction to D3.js
Having fun with graphs, a short introduction to D3.jsMichael Hackstein
 
Where20 2008 Ruby Tutorial
Where20 2008 Ruby TutorialWhere20 2008 Ruby Tutorial
Where20 2008 Ruby TutorialShoaib Burq
 
Askayworkshop
AskayworkshopAskayworkshop
Askayworkshopsconnin
 
Stupid Canvas Tricks
Stupid Canvas TricksStupid Canvas Tricks
Stupid Canvas Tricksdeanhudson
 
“A Quaint and Curious Volume of Forgotten Lore,” or an Exercise in Digital Hu...
“A Quaint and Curious Volume of Forgotten Lore,” or an Exercise in Digital Hu...“A Quaint and Curious Volume of Forgotten Lore,” or an Exercise in Digital Hu...
“A Quaint and Curious Volume of Forgotten Lore,” or an Exercise in Digital Hu...Dmitry Zinoviev
 
HTML5 Canvas - The Future of Graphics on the Web
HTML5 Canvas - The Future of Graphics on the WebHTML5 Canvas - The Future of Graphics on the Web
HTML5 Canvas - The Future of Graphics on the WebRobin Hawkes
 
Geospatial Enhancements in MongoDB 2.4
Geospatial Enhancements in MongoDB 2.4Geospatial Enhancements in MongoDB 2.4
Geospatial Enhancements in MongoDB 2.4MongoDB
 
Machine Tags
Machine TagsMachine Tags
Machine Tagshchen1
 
5 tips for your HTML5 games
5 tips for your HTML5 games5 tips for your HTML5 games
5 tips for your HTML5 gamesErnesto Jiménez
 
HTML5 Canvas - Let's Draw!
HTML5 Canvas - Let's Draw!HTML5 Canvas - Let's Draw!
HTML5 Canvas - Let's Draw!Phil Reither
 

What's hot (20)

Wikimapia mari kita jelaskan seluruh dunia!
Wikimapia   mari kita jelaskan seluruh dunia!Wikimapia   mari kita jelaskan seluruh dunia!
Wikimapia mari kita jelaskan seluruh dunia!
 
09 Simulation
09 Simulation09 Simulation
09 Simulation
 
Introduction to HTML5 Canvas
Introduction to HTML5 CanvasIntroduction to HTML5 Canvas
Introduction to HTML5 Canvas
 
Grails : Ordr, Maps & Charts
Grails : Ordr, Maps & ChartsGrails : Ordr, Maps & Charts
Grails : Ordr, Maps & Charts
 
HTML5 Canvas
HTML5 CanvasHTML5 Canvas
HTML5 Canvas
 
Exploring Canvas
Exploring CanvasExploring Canvas
Exploring Canvas
 
Google Maps JS API
Google Maps JS APIGoogle Maps JS API
Google Maps JS API
 
Swift Map
Swift MapSwift Map
Swift Map
 
Having fun with graphs, a short introduction to D3.js
Having fun with graphs, a short introduction to D3.jsHaving fun with graphs, a short introduction to D3.js
Having fun with graphs, a short introduction to D3.js
 
Where20 2008 Ruby Tutorial
Where20 2008 Ruby TutorialWhere20 2008 Ruby Tutorial
Where20 2008 Ruby Tutorial
 
Askayworkshop
AskayworkshopAskayworkshop
Askayworkshop
 
Geolocation and Mapping
Geolocation and MappingGeolocation and Mapping
Geolocation and Mapping
 
Stupid Canvas Tricks
Stupid Canvas TricksStupid Canvas Tricks
Stupid Canvas Tricks
 
“A Quaint and Curious Volume of Forgotten Lore,” or an Exercise in Digital Hu...
“A Quaint and Curious Volume of Forgotten Lore,” or an Exercise in Digital Hu...“A Quaint and Curious Volume of Forgotten Lore,” or an Exercise in Digital Hu...
“A Quaint and Curious Volume of Forgotten Lore,” or an Exercise in Digital Hu...
 
HTML5 Canvas - The Future of Graphics on the Web
HTML5 Canvas - The Future of Graphics on the WebHTML5 Canvas - The Future of Graphics on the Web
HTML5 Canvas - The Future of Graphics on the Web
 
Geospatial Enhancements in MongoDB 2.4
Geospatial Enhancements in MongoDB 2.4Geospatial Enhancements in MongoDB 2.4
Geospatial Enhancements in MongoDB 2.4
 
Variables
VariablesVariables
Variables
 
Machine Tags
Machine TagsMachine Tags
Machine Tags
 
5 tips for your HTML5 games
5 tips for your HTML5 games5 tips for your HTML5 games
5 tips for your HTML5 games
 
HTML5 Canvas - Let's Draw!
HTML5 Canvas - Let's Draw!HTML5 Canvas - Let's Draw!
HTML5 Canvas - Let's Draw!
 

Viewers also liked

Architecture at SimpleGeo: Staying Agile at Scale
Architecture at SimpleGeo: Staying Agile at ScaleArchitecture at SimpleGeo: Staying Agile at Scale
Architecture at SimpleGeo: Staying Agile at ScaleMike Malone
 
Scalability, Availability & Stability Patterns
Scalability, Availability & Stability PatternsScalability, Availability & Stability Patterns
Scalability, Availability & Stability PatternsJonas Bonér
 
Participatory working in the U.K.
Participatory working in the U.K.Participatory working in the U.K.
Participatory working in the U.K.David Barrie
 
การทำ Flash popup menu v.2
การทำ Flash popup menu v.2การทำ Flash popup menu v.2
การทำ Flash popup menu v.2Augusts Programmer
 
Django Admin (Python meeutp)
Django Admin (Python meeutp)Django Admin (Python meeutp)
Django Admin (Python meeutp)Ines Jelovac
 
Django Introduction & Tutorial
Django Introduction & TutorialDjango Introduction & Tutorial
Django Introduction & Tutorial之宇 趙
 
Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!Eric Palakovich Carr
 
Getting Started With Django
Getting Started With DjangoGetting Started With Django
Getting Started With Djangojeff_croft
 
Starters with Django
Starters with Django Starters with Django
Starters with Django BeDjango
 
Must Know Google Map Features for your Web application
Must Know Google Map Features  for your Web applicationMust Know Google Map Features  for your Web application
Must Know Google Map Features for your Web applicationAppsbee
 
Django 實戰 - 自己的購物網站自己做
Django 實戰 - 自己的購物網站自己做Django 實戰 - 自己的購物網站自己做
Django 實戰 - 自己的購物網站自己做flywindy
 
Order Out of Chaos - The Designs of Donna Karan
Order Out of Chaos - The Designs of Donna KaranOrder Out of Chaos - The Designs of Donna Karan
Order Out of Chaos - The Designs of Donna KaranGraspingfish
 
Donna Karan Collection
Donna Karan CollectionDonna Karan Collection
Donna Karan Collectionrichmasney
 

Viewers also liked (19)

Architecture at SimpleGeo: Staying Agile at Scale
Architecture at SimpleGeo: Staying Agile at ScaleArchitecture at SimpleGeo: Staying Agile at Scale
Architecture at SimpleGeo: Staying Agile at Scale
 
Scalability, Availability & Stability Patterns
Scalability, Availability & Stability PatternsScalability, Availability & Stability Patterns
Scalability, Availability & Stability Patterns
 
Participatory working in the U.K.
Participatory working in the U.K.Participatory working in the U.K.
Participatory working in the U.K.
 
Planning for the Commonwealth’s Economic Rebound
Planning for the Commonwealth’s Economic Rebound Planning for the Commonwealth’s Economic Rebound
Planning for the Commonwealth’s Economic Rebound
 
การทำ Flash popup menu v.2
การทำ Flash popup menu v.2การทำ Flash popup menu v.2
การทำ Flash popup menu v.2
 
Django Admin (Python meeutp)
Django Admin (Python meeutp)Django Admin (Python meeutp)
Django Admin (Python meeutp)
 
Free django
Free djangoFree django
Free django
 
Django Introduction & Tutorial
Django Introduction & TutorialDjango Introduction & Tutorial
Django Introduction & Tutorial
 
Django Heresies
Django HeresiesDjango Heresies
Django Heresies
 
Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!
 
Getting Started With Django
Getting Started With DjangoGetting Started With Django
Getting Started With Django
 
Starters with Django
Starters with Django Starters with Django
Starters with Django
 
Must Know Google Map Features for your Web application
Must Know Google Map Features  for your Web applicationMust Know Google Map Features  for your Web application
Must Know Google Map Features for your Web application
 
Django 實戰 - 自己的購物網站自己做
Django 實戰 - 自己的購物網站自己做Django 實戰 - 自己的購物網站自己做
Django 實戰 - 自己的購物網站自己做
 
Order Out of Chaos - The Designs of Donna Karan
Order Out of Chaos - The Designs of Donna KaranOrder Out of Chaos - The Designs of Donna Karan
Order Out of Chaos - The Designs of Donna Karan
 
Asmita Marwa
Asmita MarwaAsmita Marwa
Asmita Marwa
 
Jimmmy
JimmmyJimmmy
Jimmmy
 
Donna Karan Collection
Donna Karan CollectionDonna Karan Collection
Donna Karan Collection
 
Jimmmy
JimmmyJimmmy
Jimmmy
 

Similar to The Web map stack on Django

SVCC 2013 D3.js Presentation (10/05/2013)
SVCC 2013 D3.js Presentation (10/05/2013)SVCC 2013 D3.js Presentation (10/05/2013)
SVCC 2013 D3.js Presentation (10/05/2013)Oswald Campesato
 
ggtimeseries-->ggplot2 extensions
ggtimeseries-->ggplot2 extensions ggtimeseries-->ggplot2 extensions
ggtimeseries-->ggplot2 extensions Dr. Volkan OBAN
 
State of the Art Web Mapping with Open Source
State of the Art Web Mapping with Open SourceState of the Art Web Mapping with Open Source
State of the Art Web Mapping with Open SourceOSCON Byrum
 
How to build a html5 websites.v1
How to build a html5 websites.v1How to build a html5 websites.v1
How to build a html5 websites.v1Bitla Software
 
The State of JavaScript (2015)
The State of JavaScript (2015)The State of JavaScript (2015)
The State of JavaScript (2015)Domenic Denicola
 
Visual Exploration of Large Data sets with D3, crossfilter and dc.js
Visual Exploration of Large Data sets with D3, crossfilter and dc.jsVisual Exploration of Large Data sets with D3, crossfilter and dc.js
Visual Exploration of Large Data sets with D3, crossfilter and dc.jsFlorian Georg
 
All I know about rsc.io/c2go
All I know about rsc.io/c2goAll I know about rsc.io/c2go
All I know about rsc.io/c2goMoriyoshi Koizumi
 
Web+GISという視点から見たGISの方向性
Web+GISという視点から見たGISの方向性Web+GISという視点から見たGISの方向性
Web+GISという視点から見たGISの方向性Hidenori Fujimura
 
Functional programming using underscorejs
Functional programming using underscorejsFunctional programming using underscorejs
Functional programming using underscorejs偉格 高
 
Graph computation
Graph computationGraph computation
Graph computationSigmoid
 
Better d3 charts with tdd
Better d3 charts with tddBetter d3 charts with tdd
Better d3 charts with tddMarcos Iglesias
 
MiamiJS - The Future of JavaScript
MiamiJS - The Future of JavaScriptMiamiJS - The Future of JavaScript
MiamiJS - The Future of JavaScriptCaridy Patino
 
Tom Colley - QGIS Print Composer & Advanced Atlas Production
Tom Colley - QGIS Print Composer & Advanced Atlas ProductionTom Colley - QGIS Print Composer & Advanced Atlas Production
Tom Colley - QGIS Print Composer & Advanced Atlas ProductionShaun Lewis
 

Similar to The Web map stack on Django (20)

Supstat nyc subway
Supstat nyc subwaySupstat nyc subway
Supstat nyc subway
 
dojo.Patterns
dojo.Patternsdojo.Patterns
dojo.Patterns
 
Svcc 2013-d3
Svcc 2013-d3Svcc 2013-d3
Svcc 2013-d3
 
SVCC 2013 D3.js Presentation (10/05/2013)
SVCC 2013 D3.js Presentation (10/05/2013)SVCC 2013 D3.js Presentation (10/05/2013)
SVCC 2013 D3.js Presentation (10/05/2013)
 
ES6 is Nigh
ES6 is NighES6 is Nigh
ES6 is Nigh
 
ggtimeseries-->ggplot2 extensions
ggtimeseries-->ggplot2 extensions ggtimeseries-->ggplot2 extensions
ggtimeseries-->ggplot2 extensions
 
State of the Art Web Mapping with Open Source
State of the Art Web Mapping with Open SourceState of the Art Web Mapping with Open Source
State of the Art Web Mapping with Open Source
 
How to build a html5 websites.v1
How to build a html5 websites.v1How to build a html5 websites.v1
How to build a html5 websites.v1
 
The State of JavaScript (2015)
The State of JavaScript (2015)The State of JavaScript (2015)
The State of JavaScript (2015)
 
Visual Exploration of Large Data sets with D3, crossfilter and dc.js
Visual Exploration of Large Data sets with D3, crossfilter and dc.jsVisual Exploration of Large Data sets with D3, crossfilter and dc.js
Visual Exploration of Large Data sets with D3, crossfilter and dc.js
 
All I know about rsc.io/c2go
All I know about rsc.io/c2goAll I know about rsc.io/c2go
All I know about rsc.io/c2go
 
Web+GISという視点から見たGISの方向性
Web+GISという視点から見たGISの方向性Web+GISという視点から見たGISの方向性
Web+GISという視点から見たGISの方向性
 
Functional programming using underscorejs
Functional programming using underscorejsFunctional programming using underscorejs
Functional programming using underscorejs
 
Graph computation
Graph computationGraph computation
Graph computation
 
A More Flash Like Web?
A More Flash Like Web?A More Flash Like Web?
A More Flash Like Web?
 
Better d3 charts with tdd
Better d3 charts with tddBetter d3 charts with tdd
Better d3 charts with tdd
 
Introduction to R
Introduction to RIntroduction to R
Introduction to R
 
MiamiJS - The Future of JavaScript
MiamiJS - The Future of JavaScriptMiamiJS - The Future of JavaScript
MiamiJS - The Future of JavaScript
 
Tom Colley - QGIS Print Composer & Advanced Atlas Production
Tom Colley - QGIS Print Composer & Advanced Atlas ProductionTom Colley - QGIS Print Composer & Advanced Atlas Production
Tom Colley - QGIS Print Composer & Advanced Atlas Production
 
jQuery Loves You
jQuery Loves YoujQuery Loves You
jQuery Loves You
 

Recently uploaded

WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 

Recently uploaded (20)

WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 

The Web map stack on Django

  • 1. The Web map stack on Django Paul Smith http://www.pauladamsmith.com/ @paulsmith EveryBlock EuroDjangoCon ‘09
  • 2.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9. 11 metros Boston Philadelphia ● ● Charlotte San Francisco ● ● Chicago San Jose ● ● Los Angeles Seattle ● ● Miami Washington, DC ● ● New York … and growing ●
  • 11. Why?
  • 14. The Web map stack
  • 15. The Web map stack
  • 16. The Web map stack
  • 17. The Web map stack
  • 18. The Web map stack
  • 19. GeoDjango + Mapnik example app “Your Political Footprint”
  • 21.
  • 22.
  • 23. # models.py from django.contrib.gis.db import models class CongressionalDistrict(models.Model): state = models.ForeignKey(State) name = models.CharField(max_length=32) # ex. 1st, 25th, at-large number = models.IntegerField() # 0 if at-large district = models.MultiPolygonField(srid=4326) objects = models.GeoManager() def __unicode__(self): return '%s %s' % (self.state.name, self.name) class Footprint(models.Model): location = models.CharField(max_length=200) point = models.PointField(srid=4326) cong_dist = models.ForeignKey(CongressionalDistrict) objects = models.GeoManager() def __unicode__(self): return '%s in %s' % (self.location, self.cong_dist)
  • 24.
  • 26. # urls.py from django.conf import settings from django.conf.urls.defaults import * from edc_demo.footprint import views urlpatterns = patterns('', (r'^footprint/', views.political_footprint), (r'^tile/', views.map_tile) )
  • 27. # views.py from mapnik import * from django.http import HttpResponse, Http404 from django.conf import settings from edc_demo.footprint.models import CongressionalDistrict TILE_WIDTH = TILE_HEIGHT = 256 TILE_MIMETYPE = 'image/png' LIGHT_GREY = '#C0CCC4' PGIS_DB_CONN = dict( host=settings.DATABASE_HOST, dbname=settings.DATABASE_NAME, user=settings.DATABASE_USER, password=settings.DATABASE_PASSWORD) def map_tile(request): if request.GET.has_key('bbox'): bbox = [float(x) for x in request.GET['bbox'].split(',')] tile = Map(TILE_WIDTH, TILE_HEIGHT) rule = Rule() rule.symbols.append(LineSymbolizer(Color(LIGHT_GREY), 1.0)) style = Style() style.rules.append(rule) tile.append_style('cong_dist', style) layer = Layer('cong_dists') db_table = CongressionalDistrict._meta.db_table layer.datasource = PostGIS(table=db_table, **PGIS_DB_CONN) layer.styles.append('cong_dist') tile.layers.append(layer) tile.zoom_to_box(Envelope(*bbox)) img = Image(tile.width, tile.height) render(tile, img) img_bytes = img.tostring(TILE_MIMETYPE.split('/')[1]) return HttpResponse(img_bytes, mimetype=TILE_MIMETYPE) else: raise Http404()
  • 28. # views.py cont'd from django.shortcuts import render_to_response from edc_demo.footprint.geocoder import geocode def political_footprint(request): context = {} if request.GET.has_key('location'): point = geocode(request.GET['location']) cd = CongressionalDistrict.objects.get(district__contains=point) footprint = Footprint.objects.create( location = request.GET['location'], point = point, cong_dist = cd ) context['footprint'] = footprint context['cd_bbox'] = cong_dist.district.extent return render_to_response('footprint.html', context)
  • 29. // footprint.html <script type=quot;text/javascriptquot;> var map; var TileLayerClass = OpenLayers.Class(OpenLayers.Layer.TMS, { initialize: function(footprint_id) { var name = quot;tilesquot;; var url = quot;http://127.0.0.1:8000/tile/quot;; var args = []; args.push(name, url, {}, {}); OpenLayers.Layer.Grid.prototype.initialize.apply(this, args); this.footprint_id = footprint_id; }, getURL: function(bounds) { var url = this.url + quot;?bbox=quot; + bounds.toBBOX(); if (this.footprint_id) url += quot;&fp_id=quot; + this.footprint_id; return url; } }); function onload() { var options = { minScale: 19660800, numZoomLevels: 14, units: quot;degreesquot; }; map = new OpenLayers.Map(quot;mapquot;); {% if not footprint %} var bbox = new OpenLayers.Bounds(-126.298828, 17.578125, -64.775391, 57.128906); var tileLayer = new TileLayerClass(); {% else %} var bbox = new OpenLayers.Bounds({{ cd_bbox|join:quot;, quot; }}); var tileLayer = new TileLayerClass({{ footprint.id }}); {% endif %} map.addLayer(tileLayer); map.zoomToExtent(bbox); }
  • 30. # views.py from edc_demo.footprint.models import Footprint def map_tile(request): if request.GET.has_key('bbox'): bbox = [float(x) for x in request.GET['bbox'].split(',')] tile = Map(TILE_WIDTH, TILE_HEIGHT) rule = Rule() rule.symbols.append(LineSymbolizer(Color(LIGHT_GREY), 1.0)) style = Style() style.rules.append(rule) if request.GET.has_key('fp_id'): footprint = Footprint.objects.get(pk=request.GET['fp_id']) rule = Rule() rule.symbols.append(LineSymbolizer(Color(GREEN), 1.0)) rule.symbols.append(PolygonSymbolizer(Color(LIGHT_GREEN))) rule.filter = Filter('[id] = ' + str(footprint.cong_dist.id)) style.rules.append(rule) tile.append_style('cong_dist', style) layer = Layer('cong_dists') db_table = CongressionalDistrict._meta.db_table layer.datasource = PostGIS(table=db_table, **PGIS_DB_CONN) layer.styles.append('cong_dist') tile.layers.append(layer) if request.GET.has_key('fp_id'): add_footprint_layer(tile, footprint) tile.zoom_to_box(Envelope(*bbox)) img = Image(tile.width, tile.height) render(tile, img) img_bytes = img.tostring(TILE_MIMETYPE.split('/')[1]) return HttpResponse(img_bytes, mimetype=TILE_MIMETYPE) else: raise Http404()
  • 31. # views.py cont'd def add_footprint_layer(tile, footprint): rule = Rule() rule.symbols.append( PointSymbolizer( os.path.join(settings.STATIC_MEDIA_DIR, 'img', 'footprint.png'), 'png', 46, 46) ) rule.filter = Filter('[id] = ' + str(footprint.id)) style = Style() style.rules.append(rule) tile.append_style('footprint', style) layer = Layer('footprint') layer.datasource = PostGIS(table=Footprint._meta.db_table, **PGIS_DB_CONN) layer.styles.append('footprint') tile.layers.append(layer)
  • 32.
  • 35. Tile example z: 5, x: 2384, y: 1352
  • 36. TileCache pro con Cache population Python overhead ● ● integrated with (rendering, serving) request/response cycle Flexible storage ●
  • 37. Pre-render + custom nginx mod pro con Fast responses Render everything in ● ● advance Parallelizable, offline ● rendering C module inflexibility ● (esp. storage backends)
  • 38. Tile rendering for each zoom level z: for each column x: for each row y: render tile (x, y, z)
  • 39. Tile rendering for each zoom level z: for each column x: for each row y: render tile (x, y, z)
  • 40. Tile rendering for each zoom level z: for each column x: for each row y: render tile (x, y, z)
  • 41. Tile rendering for each zoom level z: for each column x: for each row y: render tile (x, y, z)
  • 42. # nginx.conf server { server_name tile.example.com root /var/www/maptiles; expires max; location ~* ^/[^/]+/w+/d+/d+,d+.(jpg|gif|png)$ { tilecache; } }
  • 43. // 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;);
  • 44. // 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); }
  • 45. Custom tile cache technique responsibility Far-future expiry Tile versions for cache ● ● header expires max; invalidation
  • 46. // everyblock.js eb.TileLayer = OpenLayers.Class(OpenLayers.Layer.TMS, { version: null, // see eb.TILE_VERSION layername: null, // lower-cased: quot;mainquot;, quot;locatorquot; type: null, // i.e., mime-type extension: quot;pngquot;, quot;jpgquot;, quot;gifquot; initialize: function(name, url, options) { var args = []; args.push(name, url, {}, options); OpenLayers.Layer.TMS.prototype.initialize.apply(this, args); }, // Returns an object with the x, y, and z of a tile for a given bounds getCoordinate: function(bounds) { bounds = this.adjustBounds(bounds); var res = this.map.getResolution(); var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h)); var z = this.map.getZoom(); return {x: x, y: y, z: z}; }, getPath: function(x, y, z) { return this.version + quot;/quot; + this.layername + quot;/quot; + z + quot;/quot; + x + quot;,quot; + y + quot;.quot; + this.type; }, getURL: function(bounds) { var coord = this.getCoordinate(bounds); var path = this.getPath(coord.x, coord.y, coord.z); var url = this.url; if (url instanceof Array) url = this.selectUrl(path, url); return url + path; }, CLASS_NAME: quot;eb.TileLayerquot; });
  • 48.
  • 49.
  • 50. # 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
  • 51. # 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))
  • 52. # 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
  • 56. Thank you http://www.pauladamsmith.com/ @paulsmith paulsmith@gmail.com Further exploration: “How to Lie with Maps” Mark Monmonier