Skip to content

Commit

Permalink
update to latest grid rendering api in mapnik and add modified genera…
Browse files Browse the repository at this point in the history
…te_tiles.py rather than patching
  • Loading branch information
Dane Springmeyer committed Jun 4, 2011
1 parent 8d0c905 commit 0f3403e
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 24 deletions.
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.DS_Store
OpenLayers.js
generate_tiles.py
jquery-1.5.2.min.js
wax.ol.js
build/
ext/
tiles/
27 changes: 16 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ install:
echo 'nothing to install, just run 'make' then open index.html in a browser!'

gridsforkids:
curl -o OpenLayers.js http://openlayers.org/api/OpenLayers.js
curl -o jquery-1.5.2.min.js http://code.jquery.com/jquery-1.5.2.min.js
curl -o wax.ol.js https://github.com/mapbox/wax/raw/master/build/wax.ol.js
curl -o generate_tiles.py http://svn.openstreetmap.org/applications/rendering/mapnik/generate_tiles.py
patch -p0 < generate_tiles.diff
chmod +x generate_tiles.py
mkdir -p ext/
curl -o ext/OpenLayers.js https://github.com/mapbox/wax/raw/master/ext/OpenLayers.js
curl -o ext/modestmaps.js https://github.com/mapbox/wax/raw/master/ext/modestmaps.js
curl -o ext/jquery-1.5.min.js https://github.com/mapbox/wax/raw/master/ext/jquery-1.5.min.js
curl -o ext/reqwest.min.js https://github.com/mapbox/wax/raw/master/ext/reqwest.min.js
mkdir -p build
curl -o build/wax.ol.min.js https://github.com/mapbox/wax/raw/master/build/wax.ol.min.js
curl -o build/wax.ol.js https://github.com/mapbox/wax/raw/master/build/wax.ol.js
curl -o build/wax.mm.min.js https://github.com/mapbox/wax/raw/master/build/wax.mm.min.js
#curl -o generate_tiles.py http://svn.openstreetmap.org/applications/rendering/mapnik/generate_tiles.py
#patch -p0 < generate_tiles.diff
#chmod +x generate_tiles.py

tiles:
mkdir -p tiles/1.0.0/world/
Expand All @@ -18,11 +24,10 @@ tiles:
echo 'now you can open the index.html!'

clean:
if test -e "tiles"; then rm -r "tiles"; fi
if test -e "generate_tiles.py"; then rm "generate_tiles.py"; fi
if test -e "jquery-1.5.2.min.js"; then rm "jquery-1.5.2.min.js"; fi
if test -e "OpenLayers.js"; then rm "OpenLayers.js"; fi
if test -e "wax.ol.js"; then rm "wax.ol.js"; fi
if test -e "./tiles"; then rm -r "./tiles"; fi
if test -e "./ext/"; then rm -r "./ext/"; fi
if test -e "./build/"; then rm -r "./build/"; fi
#if test -e "generate_tiles.py"; then rm "generate_tiles.py"; fi

test:
open -a Safari index.html
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,14 @@ An illustrative sample that shows simple usage of Wax and Mapnik's grid_renderer

## Depends

Mapnik trunk >= r 2842

subversion (to checkout generate_tiles.py)

patch (to patch generate_tiles.py)
Mapnik trunk >= r 2957

python >= 2.6 (for built in json module)


## Installation

Install Mapnik from >= r2842. More details about installing the latest mapnik at: http://trac.mapnik.org/wiki/Mapnik2
Install Mapnik from >= r2957. More details about installing the latest mapnik at: http://trac.mapnik.org/wiki/Mapnik2


## Setup
Expand All @@ -43,4 +39,4 @@ An illustrative sample that shows simple usage of Wax and Mapnik's grid_renderer

$ make tiles

Then open the index.html in a web browser and you should be able to hover over polygons and get the country names.
Then open the index.html in a web browser and you should be able to hover over polygons and get the country names and population.
261 changes: 261 additions & 0 deletions generate_tiles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
#!/usr/bin/python
from math import pi,cos,sin,log,exp,atan
from subprocess import call
import sys, os
from Queue import Queue
import mapnik2 as mapnik
import threading
import json

DEG_TO_RAD = pi/180
RAD_TO_DEG = 180/pi

# Default number of rendering threads to spawn, should be roughly equal to number of CPU cores available
NUM_THREADS = 4


def minmax (a,b,c):
a = max(a,b)
a = min(a,c)
return a

class GoogleProjection:
def __init__(self,levels=18):
self.Bc = []
self.Cc = []
self.zc = []
self.Ac = []
c = 256
for d in range(0,levels):
e = c/2;
self.Bc.append(c/360.0)
self.Cc.append(c/(2 * pi))
self.zc.append((e,e))
self.Ac.append(c)
c *= 2

def fromLLtoPixel(self,ll,zoom):
d = self.zc[zoom]
e = round(d[0] + ll[0] * self.Bc[zoom])
f = minmax(sin(DEG_TO_RAD * ll[1]),-0.9999,0.9999)
g = round(d[1] + 0.5*log((1+f)/(1-f))*-self.Cc[zoom])
return (e,g)

def fromPixelToLL(self,px,zoom):
e = self.zc[zoom]
f = (px[0] - e[0])/self.Bc[zoom]
g = (px[1] - e[1])/-self.Cc[zoom]
h = RAD_TO_DEG * ( 2 * atan(exp(g)) - 0.5 * pi)
return (f,h)



class RenderThread:
def __init__(self, tile_dir, mapfile, q, printLock, maxZoom):
self.tile_dir = tile_dir
self.q = q
self.m = mapnik.Map(256, 256)
self.printLock = printLock
# Load style XML
mapnik.load_map(self.m, mapfile, True)
# Obtain <Map> projection
self.prj = mapnik.Projection(self.m.srs)
# Projects between tile pixel co-ordinates and LatLong (EPSG:4326)
self.tileproj = GoogleProjection(maxZoom+1)


def render_tile(self, tile_uri, x, y, z):

# Calculate pixel positions of bottom-left & top-right
p0 = (x * 256, (y + 1) * 256)
p1 = ((x + 1) * 256, y * 256)

# Convert to LatLong (EPSG:4326)
l0 = self.tileproj.fromPixelToLL(p0, z);
l1 = self.tileproj.fromPixelToLL(p1, z);

# Convert to map projection (e.g. mercator co-ords EPSG:900913)
c0 = self.prj.forward(mapnik.Coord(l0[0],l0[1]))
c1 = self.prj.forward(mapnik.Coord(l1[0],l1[1]))

# Bounding box for the tile
if hasattr(mapnik,'mapnik_version') and mapnik.mapnik_version() >= 800:
bbox = mapnik.Box2d(c0.x,c0.y, c1.x,c1.y)
else:
bbox = mapnik.Envelope(c0.x,c0.y, c1.x,c1.y)
render_size = 256
self.m.resize(render_size, render_size)
self.m.zoom_to_box(bbox)
self.m.buffer_size = 128

# Render image with default Agg renderer
im = mapnik.Image(render_size, render_size)
mapnik.render(self.m, im)
im.save(tile_uri, 'png256')
grid_uri = tile_uri.replace('.png','.grid.json')
grid = mapnik.Grid(render_size, render_size)
mapnik.render_layer(self.m,grid,layer=0,fields=['POP2005','NAME'])
grid_utf = grid.encode()
#grid_utf = mapnik.render_grid(self.m,0,key='__id__',resolution=4,fields=['POP2005','NAME'])
# client code uses jsonp, so fake by wrapping in grid() callback
open(grid_uri,'wb').write('grid(' + json.dumps(grid_utf) + ')')



def loop(self):
while True:
#Fetch a tile from the queue and render it
r = self.q.get()
if (r == None):
self.q.task_done()
break
else:
(name, tile_uri, x, y, z) = r

exists= ""
if os.path.isfile(tile_uri):
exists= "exists"
else:
self.render_tile(tile_uri, x, y, z)
bytes=os.stat(tile_uri)[6]
empty= ''
if bytes == 103:
empty = " Empty Tile "
self.printLock.acquire()
print name, ":", z, x, y, exists, empty
self.printLock.release()
self.q.task_done()



def render_tiles(bbox, mapfile, tile_dir, minZoom=1,maxZoom=18, name="unknown", num_threads=NUM_THREADS, tms_scheme=False):
print "render_tiles(",bbox, mapfile, tile_dir, minZoom,maxZoom, name,")"

# Launch rendering threads
queue = Queue(32)
printLock = threading.Lock()
renderers = {}
for i in range(num_threads):
renderer = RenderThread(tile_dir, mapfile, queue, printLock, maxZoom)
render_thread = threading.Thread(target=renderer.loop)
render_thread.start()
#print "Started render thread %s" % render_thread.getName()
renderers[i] = render_thread

if not os.path.isdir(tile_dir):
os.mkdir(tile_dir)

gprj = GoogleProjection(maxZoom+1)

ll0 = (bbox[0],bbox[3])
ll1 = (bbox[2],bbox[1])

for z in range(minZoom,maxZoom + 1):
px0 = gprj.fromLLtoPixel(ll0,z)
px1 = gprj.fromLLtoPixel(ll1,z)

# check if we have directories in place
zoom = "%s" % z
if not os.path.isdir(tile_dir + zoom):
os.mkdir(tile_dir + zoom)
for x in range(int(px0[0]/256.0),int(px1[0]/256.0)+1):
# Validate x co-ordinate
if (x < 0) or (x >= 2**z):
continue
# check if we have directories in place
str_x = "%s" % x
if not os.path.isdir(tile_dir + zoom + '/' + str_x):
os.mkdir(tile_dir + zoom + '/' + str_x)
for y in range(int(px0[1]/256.0),int(px1[1]/256.0)+1):
# Validate x co-ordinate
if (y < 0) or (y >= 2**z):
continue
# flip y to match OSGEO TMS spec
if tms_scheme:
str_y = "%s" % ((2**z-1) - y)
else:
str_y = "%s" % y
tile_uri = tile_dir + zoom + '/' + str_x + '/' + str_y + '.png'
# Submit tile to be rendered into the queue
t = (name, tile_uri, x, y, z)
try:
queue.put(t)
except KeyboardInterrupt:
raise SystemExit("Ctrl-c detected, exiting...")

# Signal render threads to exit by sending empty request to queue
for i in range(num_threads):
queue.put(None)
# wait for pending rendering jobs to complete
queue.join()
for i in range(num_threads):
renderers[i].join()



if __name__ == "__main__":
home = os.environ['HOME']
try:
mapfile = os.environ['MAPNIK_MAP_FILE']
except KeyError:
mapfile = home + "/svn.openstreetmap.org/applications/rendering/mapnik/osm-local.xml"
try:
tile_dir = os.environ['MAPNIK_TILE_DIR']
except KeyError:
tile_dir = home + "/osm/tiles/"

if not tile_dir.endswith('/'):
tile_dir = tile_dir + '/'

#-------------------------------------------------------------------------
#
# Change the following for different bounding boxes and zoom levels
#
# Start with an overview
# World
bbox = (-180.0,-90.0, 180.0,90.0)

render_tiles(bbox, mapfile, tile_dir, 0, 3, "World",tms_scheme=True)

'''
minZoom = 10
maxZoom = 16
bbox = (-2, 50.0,1.0,52.0)
render_tiles(bbox, mapfile, tile_dir, minZoom, maxZoom)
# Muenchen
bbox = (11.4,48.07, 11.7,48.22)
render_tiles(bbox, mapfile, tile_dir, 1, 12 , "Muenchen")
# Muenchen+
bbox = (11.3,48.01, 12.15,48.44)
render_tiles(bbox, mapfile, tile_dir, 7, 12 , "Muenchen+")
# Muenchen++
bbox = (10.92,47.7, 12.24,48.61)
render_tiles(bbox, mapfile, tile_dir, 7, 12 , "Muenchen++")
# Nuernberg
bbox=(10.903198,49.560441,49.633534,11.038085)
render_tiles(bbox, mapfile, tile_dir, 10, 16, "Nuernberg")
# Karlsruhe
bbox=(8.179113,48.933617,8.489252,49.081707)
render_tiles(bbox, mapfile, tile_dir, 10, 16, "Karlsruhe")
# Karlsruhe+
bbox = (8.3,48.95,8.5,49.05)
render_tiles(bbox, mapfile, tile_dir, 1, 16, "Karlsruhe+")
# Augsburg
bbox = (8.3,48.95,8.5,49.05)
render_tiles(bbox, mapfile, tile_dir, 1, 16, "Augsburg")
# Augsburg+
bbox=(10.773251,48.369594,10.883834,48.438577)
render_tiles(bbox, mapfile, tile_dir, 10, 14, "Augsburg+")
# Europe+
bbox = (1.0,10.0, 20.6,50.0)
render_tiles(bbox, mapfile, tile_dir, 1, 11 , "Europe+")
'''
6 changes: 3 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@
<!--<script src="http://code.jquery.com/jquery-1.5.2.min.js" type="text/javascript"></script>-->

<!-- localized by Makefile, so we can go offline -->
<script src="jquery-1.5.2.min.js" type="text/javascript"></script>
<script src="OpenLayers.js" type="text/javascript"></script>
<script src="ext/reqwest.min.js" type="text/javascript"></script>
<script src="ext/OpenLayers.js" type="text/javascript"></script>

<!-- connector -->
<script src="wax.ol.js" type="text/javascript"></script>
<script src="build/wax.ol.min.js" type="text/javascript"></script>

<script type="text/javascript">
var map;
Expand Down
4 changes: 4 additions & 0 deletions stylesheet.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
<Datasource>
<Parameter name="file">data/world_merc.shp</Parameter>
<Parameter name="type">shape</Parameter>
<!--
<Parameter name="type">ogr</Parameter>
<Parameter name="layer_by_index">0</Parameter>
-->
</Datasource>
</Layer>

Expand Down
22 changes: 22 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env python

import os
import json
import mapnik2

m = mapnik2.Map(256,256)
mapnik2.load_map(m,'stylesheet.xml')

# tile with south america
tile = mapnik2.Box2d(-10018754.171394622,-10018754.171394622,0,0)
m.zoom_to_box(tile)

# render image
im = mapnik2.Image(m.width, m.height)
mapnik2.render(m,im)
im.save("test.png")
# render grid
grid = mapnik2.Grid(m.width, m.height)
mapnik2.render_layer(m,grid,layer=0,fields=['POP2005','NAME'])
grid_utf = grid.encode()
open('test.json','w').write(json.dumps(grid_utf))

0 comments on commit 0f3403e

Please sign in to comment.