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

GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessPixlogix Infotech
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 

Último (20)

GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 

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