-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
update to latest grid rendering api in mapnik and add modified genera…
…te_tiles.py rather than patching
- Loading branch information
Dane Springmeyer
committed
Jun 4, 2011
1 parent
8d0c905
commit 0f3403e
Showing
7 changed files
with
312 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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+") | ||
''' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |