SlideShare a Scribd company logo
PATCH MAPS
Who, What, When, Where, Why, How?
           Aubrey Holland
WHO?
•   Patch.com is a local news and information service

•   We have editorial staff in each of our 44 communities and are
    expanding to cover hundreds of communities across the country this
    year

•   Our goal is to be the equivalent of a local paper in each town, and we
    cover everything that goes on there, including everything from the
    school board meeting to restaurant reviews and obituaries

•    We also have a significant listings-gathering process that collects
    information on every business in town

•   In general, all of our data is connected to geographic information
WHAT?
WHERE?
• Patch   is in SoHo, NYC
WHEN?

• We started considering this project after seeing Paul Smith’s
 “Take Control of Your Maps” article on A List Apart in April,
 2008

• The
    project really took shape in August, 2009 and launched in
 November
WHY?

• Ourpainful design process left us with a custom page, and a
 map that looks just like everybody else’s

• Map   data at the highly local level still kinda sucks

• You   can’t integrate data directly into a Google (etc.) map
THE STACK
   Apache (mod_wsgi)


       TileCache


         Mapnik


PostGIS (with OSM planet)
APACHE CONFIG
In httpd.conf:
     LoadModule wsgi_module modules/mod_wsgi.so


In your sites-available/config:
    WSGIDaemonProcess patch processes=25 threads=1 display-
   name=%{GROUP}
    WSGIProcessGroup patch
    WSGIScriptAlias /tilecache
   /data/servers/patch_maps-fe-apache/wsgi/tilecache.wsgi



Now, you can restart the WSGI service simply by
               touching this file
TILECACHE WSGI CONFIG
This is the application that is executed with each request:
    #!/opt/bcs/python2.6
    #
    # In Apache2's config:
    # WSGIScriptAlias /tilecache /var/www/mapserver/tilecache/tilecache.wsgi
    #
    import os, sys
    tilecachepath = '/srv/data/servers/patch_maps-fe-apache/tilecache'
    sys.path.append(tilecachepath)

    from TileCache.Service import Service, wsgiHandler

    cfgfiles = (os.path.join(tilecachepath, 'tilecache.cfg'))

    theService = None
    def application(environ, start_response):
        global theService

       cfgs = cfgfiles
       if not theService:
           theService = Service.load(cfgs)
       return wsgiHandler(environ, start_response, theService)
TILECACHE CONFIG
[cache]
type=Disk
base=/data/servers/dr1nas-a-v1b/shared/common/map_tiles
expire=21600

[osm]
type=Mapnik
mapfile=/opt/bcs/packages/map_style-0.0.1/mapfile.xml
spherical_mercator=true
tms_type=google
metaTile=yes
metaSize=1,1
metaBuffer=256
levels=19
extension=png256
MAPNIK
• Mapnikrequires a mapfile telling it how to render tiles as
 requests come in

• Ourswas built using cascadenik, with help from Stamen
 Design, and we compiled the file to the mapfile format

• Thatstylesheet contains all of the queries to make against the
 database in order to render each layer, along with information
 on how to style them

• Cascadeniklets you write this in a CSS/HTML-like format,
 where the style is separated from the content
OPTIMIZING QUERIES
From this:
    (SELECT way, name FROM osm_polygon WHERE amenity IN ('school',
    'college', 'university', 'bus_station', 'ferry_terminal',
    'hospital', 'kindergarten', 'place_of_worship',
    'public_building', 'townhall') ORDER BY z_order ASC, way_area
    DESC) AS civic


To this:
    (SELECT way, name FROM osm_polygon_civic_areas_mv ORDER BY
    z_order ASC, way_area DESC) AS civic
BUILDING THE DATABASE

• Weuse the full planet.osm, though we’re only focused in
 North America at the moment

• The initial import was done by downloading the planet file
 and importing it using osm2pgsql. This took four days!!!

• We  have a replicated slave database, as well as a static copy
 that we back up to once per month
TILE_FLIP
           http://github.com/aub/tile_flip

•Uses the TileCache API to provide a simple
 interface for managing tiles
•Seeding
•Killing
•Finding
KILLING TILES
def __init__(self, tileCacheConfigFile):

def killForPoint(
    self,
    layerName,
    point,
    delta=(0.0, 0.0),
    levels=None,
    tilePadding=0):

def killForTmsPath(self, layerName, x, y, z):

def killToSize(self, maxMBs):

def killAll(self):

def killTile(self, tile):
SEEDING TILES
def __init__(self, tileCacheConfigFile, userId=-1, groupId=-1):

def seedForPoint(
    self,
    layerName,
    point,
    delta=(0.0, 0.0),
    levels=None,
    tilePadding=0,
    force=False):

def seedForTmsPath(self, layerName, x, y, z, force=False):

def seedForTile(self, tile, force=False):
FINDING TILES
def __init__(self, tileCacheConfigFile):

def findTiles(
    self,
    layerName,
    levels=None,
    bbox=None,
    tilePadding=0,
    block=None):

def isTmsPathCached(self, layerName, x, y, z):
BACKGROUND TASKS

• Apply    updates to the OSM data once per minute

• Expire   cached tiles that were affected by the data update

• Publication   updating and seeding

• Seeding   low zoom levels

• Trimming    the cache to a reasonable size
APPLYING MINUTELY
             UPDATES
  http://wiki.openstreetmap.org/wiki/Minutely_Mapnik
We have a python script that runs every minute via cron
 /opt/bcs/bin/osmosis -q --read-replication-interval
    workingDirectory=/srv/data/osm/osmosis/replication
    --write-xml-change /dev/stdout |
    /opt/bcs/bin/osm2pgsql --append --database=osm
    --username=polarmaps_rw --host=patchbe-d03.ihost.aol.com
    --port=5432 --merc --prefix=osm --slim
    --style /opt/bcs/packages/osm2pgsql-1.0.0/osm2pgsql.style
    --expire-tiles=18
    --expire-output=/srv/data/osm/expiration_lists/tmpvj1Glb
    - 2>> /srv/data/osm/log/osm2pgsql.log
APPLYING MINUTELY
              UPDATES
  osm2pgsql produces an expiration list that looks like:
                    18/42005/91478
                    18/42005/91479
                    18/42006/91478

 Our script then parses this file and adds each tile and its
  ancestors to a table in the database. A separate script
then crawls that table and uses tile_flip to expire the tiles
PUBLICATION UPDATING
            AND SEEDING
• We have a set of publications, and almost all of our tile
 requests are for areas around them

• The publications have a bounding box, and we have an API for
 getting the publication data along with it’s location information

• Anotherscript running via cron then uses tile_flip to
 automatically seed an area around each publication at all zoom
 levels
LOW ZOOM LEVEL SEEDING

• The   low zoom levels are the slowest to render

• So, wehave another script that walks levels 10-14 for the
 entire USA and pre-seeds those tiles using tile_flip
STATIC MAPS
               http://github.com/aub/static_maps/
• Once you have all of this set up, it’s amazingly simple to add
 other services, like a static map renderer

• Mapnik    has an excellent Python API you can use

• Setting
        up other services is as simple as writing a Python script
 and adding another WSGI service to Apache

• Our static map service has two endpoints, one for rendering a
 map centered around a set of points, and the second for
 returning a JSON response with the pixel positions of those
 points
PATCH IS HIRING!
QUESTIONS?
aubreyholland@gmail.com

@riotpolice

http://github.com/aub

http://www.patch.com

More Related Content

PDF
HashiCorp at Just Eat
PDF
London HUG 14/4 - Deploying and Discovering at Scale with Consul and Nomad
PDF
London HUG 8/3 - Nomad
PPTX
Gdg using docker to streamline development
PDF
Docksal: Better than VMs
PPTX
Breaking through silos - From multi to true crossplatform using the cloud
PDF
Highly available Drupal on a Raspberry Pi cluster
PPTX
BGF 2012 (Browsergames Forum)
HashiCorp at Just Eat
London HUG 14/4 - Deploying and Discovering at Scale with Consul and Nomad
London HUG 8/3 - Nomad
Gdg using docker to streamline development
Docksal: Better than VMs
Breaking through silos - From multi to true crossplatform using the cloud
Highly available Drupal on a Raspberry Pi cluster
BGF 2012 (Browsergames Forum)

What's hot (16)

PPTX
Photon Session / Unite12 Conference
PDF
Important work-arounds for making ASS multi-lingual
PPTX
Effective terraform
PPTX
Building Repeatable Infrastructure using Terraform
PDF
CI/CD with Kubernetes, Helm & Wercker (#madScalability)
PPTX
vBrownBag @ VMworld - Apache CloudStack (ACS) & vSphere
PPTX
Photon Load Test #1
PDF
EC2 Container Service
PDF
Navigation in React Native
PPTX
Beyond 1000 bosh Deployments
PPTX
Intro to Clojure 4 Developers
PDF
Altitude SF 2017: Stories from TED
PPT
What can-be-done-around-mesos
PPTX
Storage Is Not Virtualized Enough - part 1
PDF
NoSQL - Vital Open Source Ingredient for Modern Success
PPTX
Introduction to Amazon EC2 Container Service and setting up build pipeline wi...
Photon Session / Unite12 Conference
Important work-arounds for making ASS multi-lingual
Effective terraform
Building Repeatable Infrastructure using Terraform
CI/CD with Kubernetes, Helm & Wercker (#madScalability)
vBrownBag @ VMworld - Apache CloudStack (ACS) & vSphere
Photon Load Test #1
EC2 Container Service
Navigation in React Native
Beyond 1000 bosh Deployments
Intro to Clojure 4 Developers
Altitude SF 2017: Stories from TED
What can-be-done-around-mesos
Storage Is Not Virtualized Enough - part 1
NoSQL - Vital Open Source Ingredient for Modern Success
Introduction to Amazon EC2 Container Service and setting up build pipeline wi...
Ad

Similar to Patch Maps (20)

PPT
Web enabling your survey business ppt version
PPT
ScribeUI: La productivité avec MapServer
PPTX
arcgis-enterprise-caching-vector-and-raster-tiles.pptx
PDF
TechBeats #2
PDF
State of the Art Web Mapping with Open Source
PDF
Saving Money with Open Source GIS
PDF
Building Maps with Leaflet
PDF
GIS in Pharo PharoOWS & GeoView (ESUG 2025)
PPTX
Open Source Web Mapping Servers: Which horse for which course?
PDF
Implementing your own Google App Engine
PDF
Maps tek11
PPTX
First Hive Meetup London 2012-07-10 - Tomas Cervenka - VisualDNA
PDF
Integrating PostGIS in Web Applications
PPTX
Internet-enabled GIS Using Free and Open Source Tools
PDF
Social Data and Log Analysis Using MongoDB
PDF
[DBA]_HiramFleitas_SQL_PASS_Summit_2017_Summary
PDF
Spark shuffle introduction
KEY
Wider than rails
PPTX
True Reusable Code - DevSum2016
PPTX
CloudPlatforms-Cloud PLatforms evaluation
Web enabling your survey business ppt version
ScribeUI: La productivité avec MapServer
arcgis-enterprise-caching-vector-and-raster-tiles.pptx
TechBeats #2
State of the Art Web Mapping with Open Source
Saving Money with Open Source GIS
Building Maps with Leaflet
GIS in Pharo PharoOWS & GeoView (ESUG 2025)
Open Source Web Mapping Servers: Which horse for which course?
Implementing your own Google App Engine
Maps tek11
First Hive Meetup London 2012-07-10 - Tomas Cervenka - VisualDNA
Integrating PostGIS in Web Applications
Internet-enabled GIS Using Free and Open Source Tools
Social Data and Log Analysis Using MongoDB
[DBA]_HiramFleitas_SQL_PASS_Summit_2017_Summary
Spark shuffle introduction
Wider than rails
True Reusable Code - DevSum2016
CloudPlatforms-Cloud PLatforms evaluation
Ad

Recently uploaded (20)

PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Approach and Philosophy of On baking technology
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPTX
Big Data Technologies - Introduction.pptx
PDF
Modernizing your data center with Dell and AMD
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
Encapsulation theory and applications.pdf
PPT
Teaching material agriculture food technology
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Unlocking AI with Model Context Protocol (MCP)
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
Approach and Philosophy of On baking technology
Review of recent advances in non-invasive hemoglobin estimation
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Big Data Technologies - Introduction.pptx
Modernizing your data center with Dell and AMD
Network Security Unit 5.pdf for BCA BBA.
Encapsulation theory and applications.pdf
Teaching material agriculture food technology
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Digital-Transformation-Roadmap-for-Companies.pptx
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
MYSQL Presentation for SQL database connectivity
Unlocking AI with Model Context Protocol (MCP)
20250228 LYD VKU AI Blended-Learning.pptx
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Spectral efficient network and resource selection model in 5G networks
The Rise and Fall of 3GPP – Time for a Sabbatical?

Patch Maps

  • 1. PATCH MAPS Who, What, When, Where, Why, How? Aubrey Holland
  • 2. WHO? • Patch.com is a local news and information service • We have editorial staff in each of our 44 communities and are expanding to cover hundreds of communities across the country this year • Our goal is to be the equivalent of a local paper in each town, and we cover everything that goes on there, including everything from the school board meeting to restaurant reviews and obituaries • We also have a significant listings-gathering process that collects information on every business in town • In general, all of our data is connected to geographic information
  • 4. WHERE? • Patch is in SoHo, NYC
  • 5. WHEN? • We started considering this project after seeing Paul Smith’s “Take Control of Your Maps” article on A List Apart in April, 2008 • The project really took shape in August, 2009 and launched in November
  • 6. WHY? • Ourpainful design process left us with a custom page, and a map that looks just like everybody else’s • Map data at the highly local level still kinda sucks • You can’t integrate data directly into a Google (etc.) map
  • 7. THE STACK Apache (mod_wsgi) TileCache Mapnik PostGIS (with OSM planet)
  • 8. APACHE CONFIG In httpd.conf: LoadModule wsgi_module modules/mod_wsgi.so In your sites-available/config:  WSGIDaemonProcess patch processes=25 threads=1 display- name=%{GROUP}  WSGIProcessGroup patch  WSGIScriptAlias /tilecache /data/servers/patch_maps-fe-apache/wsgi/tilecache.wsgi Now, you can restart the WSGI service simply by touching this file
  • 9. TILECACHE WSGI CONFIG This is the application that is executed with each request: #!/opt/bcs/python2.6 # # In Apache2's config: # WSGIScriptAlias /tilecache /var/www/mapserver/tilecache/tilecache.wsgi # import os, sys tilecachepath = '/srv/data/servers/patch_maps-fe-apache/tilecache' sys.path.append(tilecachepath) from TileCache.Service import Service, wsgiHandler cfgfiles = (os.path.join(tilecachepath, 'tilecache.cfg')) theService = None def application(environ, start_response): global theService cfgs = cfgfiles if not theService: theService = Service.load(cfgs) return wsgiHandler(environ, start_response, theService)
  • 11. MAPNIK • Mapnikrequires a mapfile telling it how to render tiles as requests come in • Ourswas built using cascadenik, with help from Stamen Design, and we compiled the file to the mapfile format • Thatstylesheet contains all of the queries to make against the database in order to render each layer, along with information on how to style them • Cascadeniklets you write this in a CSS/HTML-like format, where the style is separated from the content
  • 12. OPTIMIZING QUERIES From this: (SELECT way, name FROM osm_polygon WHERE amenity IN ('school', 'college', 'university', 'bus_station', 'ferry_terminal', 'hospital', 'kindergarten', 'place_of_worship', 'public_building', 'townhall') ORDER BY z_order ASC, way_area DESC) AS civic To this: (SELECT way, name FROM osm_polygon_civic_areas_mv ORDER BY z_order ASC, way_area DESC) AS civic
  • 13. BUILDING THE DATABASE • Weuse the full planet.osm, though we’re only focused in North America at the moment • The initial import was done by downloading the planet file and importing it using osm2pgsql. This took four days!!! • We have a replicated slave database, as well as a static copy that we back up to once per month
  • 14. TILE_FLIP http://github.com/aub/tile_flip •Uses the TileCache API to provide a simple interface for managing tiles •Seeding •Killing •Finding
  • 15. KILLING TILES def __init__(self, tileCacheConfigFile): def killForPoint( self, layerName, point, delta=(0.0, 0.0), levels=None, tilePadding=0): def killForTmsPath(self, layerName, x, y, z): def killToSize(self, maxMBs): def killAll(self): def killTile(self, tile):
  • 16. SEEDING TILES def __init__(self, tileCacheConfigFile, userId=-1, groupId=-1): def seedForPoint( self, layerName, point, delta=(0.0, 0.0), levels=None, tilePadding=0, force=False): def seedForTmsPath(self, layerName, x, y, z, force=False): def seedForTile(self, tile, force=False):
  • 17. FINDING TILES def __init__(self, tileCacheConfigFile): def findTiles( self, layerName, levels=None, bbox=None, tilePadding=0, block=None): def isTmsPathCached(self, layerName, x, y, z):
  • 18. BACKGROUND TASKS • Apply updates to the OSM data once per minute • Expire cached tiles that were affected by the data update • Publication updating and seeding • Seeding low zoom levels • Trimming the cache to a reasonable size
  • 19. APPLYING MINUTELY UPDATES http://wiki.openstreetmap.org/wiki/Minutely_Mapnik We have a python script that runs every minute via cron /opt/bcs/bin/osmosis -q --read-replication-interval workingDirectory=/srv/data/osm/osmosis/replication --write-xml-change /dev/stdout | /opt/bcs/bin/osm2pgsql --append --database=osm --username=polarmaps_rw --host=patchbe-d03.ihost.aol.com --port=5432 --merc --prefix=osm --slim --style /opt/bcs/packages/osm2pgsql-1.0.0/osm2pgsql.style --expire-tiles=18 --expire-output=/srv/data/osm/expiration_lists/tmpvj1Glb - 2>> /srv/data/osm/log/osm2pgsql.log
  • 20. APPLYING MINUTELY UPDATES osm2pgsql produces an expiration list that looks like: 18/42005/91478 18/42005/91479 18/42006/91478 Our script then parses this file and adds each tile and its ancestors to a table in the database. A separate script then crawls that table and uses tile_flip to expire the tiles
  • 21. PUBLICATION UPDATING AND SEEDING • We have a set of publications, and almost all of our tile requests are for areas around them • The publications have a bounding box, and we have an API for getting the publication data along with it’s location information • Anotherscript running via cron then uses tile_flip to automatically seed an area around each publication at all zoom levels
  • 22. LOW ZOOM LEVEL SEEDING • The low zoom levels are the slowest to render • So, wehave another script that walks levels 10-14 for the entire USA and pre-seeds those tiles using tile_flip
  • 23. STATIC MAPS http://github.com/aub/static_maps/ • Once you have all of this set up, it’s amazingly simple to add other services, like a static map renderer • Mapnik has an excellent Python API you can use • Setting up other services is as simple as writing a Python script and adding another WSGI service to Apache • Our static map service has two endpoints, one for rendering a map centered around a set of points, and the second for returning a JSON response with the pixel positions of those points

Editor's Notes

  • #11: Configures TileCache to use a disk-based cache Expire sets the expires header in the response for client-side image caching Setting the extension to png256 compresses the tiles more efficiently Issues with meta-tiling across servers
  • #12: In the cascadenik download, you can find example working OSM stylesheets
  • #13: This first query is great. Unfortunately, there are 10,561,333 rows in osm_polygon, and the where clause can make it morbidly slow We used triggers that fire as data is added to simulate materialized views and then did our queries against those views, which is much faster
  • #20: This article explains how to do the basic setup The command: * uses osmosis to pull the latest data * pipes the output, as xml, to osm2pgsql, which appends it to our database * writes an expiration list of all tiles that would have been affected by the update