SlideShare una empresa de Scribd logo
1 de 56
Descargar para leer sin conexión
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

Más contenido relacionado

La actualidad más candente

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
 

La actualidad más candente (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!
 

Destacado

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
 

Destacado (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 a 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 a 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
 

Último

Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Alkin Tezuysal
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfpanagenda
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...Rick Flair
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentPim van der Noll
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityIES VE
 
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...panagenda
 
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
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Strongerpanagenda
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...Wes McKinney
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesKari Kakkonen
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 

Último (20)

Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a reality
 
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
 
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
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examples
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 

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