source: subversion/applications/rendering/mapnik/generate_tiles.py @ 29147

Last change on this file since 29147 was 26276, checked in by Dane Springmeyer, 8 years ago

use mapnik2 if present

  • Property svn:executable set to *
File size: 7.8 KB
Line 
1#!/usr/bin/env python
2from math import pi,cos,sin,log,exp,atan
3from subprocess import call
4import sys, os
5from Queue import Queue
6
7import threading
8
9try:
10    import mapnik2 as mapnik
11except:
12    import mapnik
13
14DEG_TO_RAD = pi/180
15RAD_TO_DEG = 180/pi
16
17# Default number of rendering threads to spawn, should be roughly equal to number of CPU cores available
18NUM_THREADS = 4
19
20
21def minmax (a,b,c):
22    a = max(a,b)
23    a = min(a,c)
24    return a
25
26class GoogleProjection:
27    def __init__(self,levels=18):
28        self.Bc = []
29        self.Cc = []
30        self.zc = []
31        self.Ac = []
32        c = 256
33        for d in range(0,levels):
34            e = c/2;
35            self.Bc.append(c/360.0)
36            self.Cc.append(c/(2 * pi))
37            self.zc.append((e,e))
38            self.Ac.append(c)
39            c *= 2
40               
41    def fromLLtoPixel(self,ll,zoom):
42         d = self.zc[zoom]
43         e = round(d[0] + ll[0] * self.Bc[zoom])
44         f = minmax(sin(DEG_TO_RAD * ll[1]),-0.9999,0.9999)
45         g = round(d[1] + 0.5*log((1+f)/(1-f))*-self.Cc[zoom])
46         return (e,g)
47     
48    def fromPixelToLL(self,px,zoom):
49         e = self.zc[zoom]
50         f = (px[0] - e[0])/self.Bc[zoom]
51         g = (px[1] - e[1])/-self.Cc[zoom]
52         h = RAD_TO_DEG * ( 2 * atan(exp(g)) - 0.5 * pi)
53         return (f,h)
54
55
56
57class RenderThread:
58    def __init__(self, tile_dir, mapfile, q, printLock, maxZoom):
59        self.tile_dir = tile_dir
60        self.q = q
61        self.m = mapnik.Map(256, 256)
62        self.printLock = printLock
63        # Load style XML
64        mapnik.load_map(self.m, mapfile, True)
65        # Obtain <Map> projection
66        self.prj = mapnik.Projection(self.m.srs)
67        # Projects between tile pixel co-ordinates and LatLong (EPSG:4326)
68        self.tileproj = GoogleProjection(maxZoom+1)
69
70
71    def render_tile(self, tile_uri, x, y, z):
72
73        # Calculate pixel positions of bottom-left & top-right
74        p0 = (x * 256, (y + 1) * 256)
75        p1 = ((x + 1) * 256, y * 256)
76
77        # Convert to LatLong (EPSG:4326)
78        l0 = self.tileproj.fromPixelToLL(p0, z);
79        l1 = self.tileproj.fromPixelToLL(p1, z);
80
81        # Convert to map projection (e.g. mercator co-ords EPSG:900913)
82        c0 = self.prj.forward(mapnik.Coord(l0[0],l0[1]))
83        c1 = self.prj.forward(mapnik.Coord(l1[0],l1[1]))
84
85        # Bounding box for the tile
86        if hasattr(mapnik,'mapnik_version') and mapnik.mapnik_version() >= 800:
87            bbox = mapnik.Box2d(c0.x,c0.y, c1.x,c1.y)
88        else:
89            bbox = mapnik.Envelope(c0.x,c0.y, c1.x,c1.y)
90        render_size = 256
91        self.m.resize(render_size, render_size)
92        self.m.zoom_to_box(bbox)
93        self.m.buffer_size = 128
94
95        # Render image with default Agg renderer
96        im = mapnik.Image(render_size, render_size)
97        mapnik.render(self.m, im)
98        im.save(tile_uri, 'png256')
99
100
101    def loop(self):
102        while True:
103            #Fetch a tile from the queue and render it
104            r = self.q.get()
105            if (r == None):
106                self.q.task_done()
107                break
108            else:
109                (name, tile_uri, x, y, z) = r
110
111            exists= ""
112            if os.path.isfile(tile_uri):
113                exists= "exists"
114            else:
115                self.render_tile(tile_uri, x, y, z)
116            bytes=os.stat(tile_uri)[6]
117            empty= ''
118            if bytes == 103:
119                empty = " Empty Tile "
120            self.printLock.acquire()
121            print name, ":", z, x, y, exists, empty
122            self.printLock.release()
123            self.q.task_done()
124
125
126
127def render_tiles(bbox, mapfile, tile_dir, minZoom=1,maxZoom=18, name="unknown", num_threads=NUM_THREADS, tms_scheme=False):
128    print "render_tiles(",bbox, mapfile, tile_dir, minZoom,maxZoom, name,")"
129
130    # Launch rendering threads
131    queue = Queue(32)
132    printLock = threading.Lock()
133    renderers = {}
134    for i in range(num_threads):
135        renderer = RenderThread(tile_dir, mapfile, queue, printLock, maxZoom)
136        render_thread = threading.Thread(target=renderer.loop)
137        render_thread.start()
138        #print "Started render thread %s" % render_thread.getName()
139        renderers[i] = render_thread
140
141    if not os.path.isdir(tile_dir):
142         os.mkdir(tile_dir)
143
144    gprj = GoogleProjection(maxZoom+1) 
145
146    ll0 = (bbox[0],bbox[3])
147    ll1 = (bbox[2],bbox[1])
148
149    for z in range(minZoom,maxZoom + 1):
150        px0 = gprj.fromLLtoPixel(ll0,z)
151        px1 = gprj.fromLLtoPixel(ll1,z)
152
153        # check if we have directories in place
154        zoom = "%s" % z
155        if not os.path.isdir(tile_dir + zoom):
156            os.mkdir(tile_dir + zoom)
157        for x in range(int(px0[0]/256.0),int(px1[0]/256.0)+1):
158            # Validate x co-ordinate
159            if (x < 0) or (x >= 2**z):
160                continue
161            # check if we have directories in place
162            str_x = "%s" % x
163            if not os.path.isdir(tile_dir + zoom + '/' + str_x):
164                os.mkdir(tile_dir + zoom + '/' + str_x)
165            for y in range(int(px0[1]/256.0),int(px1[1]/256.0)+1):
166                # Validate x co-ordinate
167                if (y < 0) or (y >= 2**z):
168                    continue
169                # flip y to match OSGEO TMS spec
170                if tms_scheme:
171                    str_y = "%s" % ((2**z-1) - y)
172                else:
173                    str_y = "%s" % y
174                tile_uri = tile_dir + zoom + '/' + str_x + '/' + str_y + '.png'
175                # Submit tile to be rendered into the queue
176                t = (name, tile_uri, x, y, z)
177                try:
178                    queue.put(t)
179                except KeyboardInterrupt:
180                    raise SystemExit("Ctrl-c detected, exiting...")
181
182    # Signal render threads to exit by sending empty request to queue
183    for i in range(num_threads):
184        queue.put(None)
185    # wait for pending rendering jobs to complete
186    queue.join()
187    for i in range(num_threads):
188        renderers[i].join()
189
190
191
192if __name__ == "__main__":
193    home = os.environ['HOME']
194    try:
195        mapfile = os.environ['MAPNIK_MAP_FILE']
196    except KeyError:
197        mapfile = home + "/svn.openstreetmap.org/applications/rendering/mapnik/osm-local.xml"
198    try:
199        tile_dir = os.environ['MAPNIK_TILE_DIR']
200    except KeyError:
201        tile_dir = home + "/osm/tiles/"
202
203    if not tile_dir.endswith('/'):
204        tile_dir = tile_dir + '/'
205
206    #-------------------------------------------------------------------------
207    #
208    # Change the following for different bounding boxes and zoom levels
209    #
210    # Start with an overview
211    # World
212    bbox = (-180.0,-90.0, 180.0,90.0)
213
214    render_tiles(bbox, mapfile, tile_dir, 0, 5, "World")
215
216    minZoom = 10
217    maxZoom = 16
218    bbox = (-2, 50.0,1.0,52.0)
219    render_tiles(bbox, mapfile, tile_dir, minZoom, maxZoom)
220
221    # Muenchen
222    bbox = (11.4,48.07, 11.7,48.22)
223    render_tiles(bbox, mapfile, tile_dir, 1, 12 , "Muenchen")
224
225    # Muenchen+
226    bbox = (11.3,48.01, 12.15,48.44)
227    render_tiles(bbox, mapfile, tile_dir, 7, 12 , "Muenchen+")
228
229    # Muenchen++
230    bbox = (10.92,47.7, 12.24,48.61)
231    render_tiles(bbox, mapfile, tile_dir, 7, 12 , "Muenchen++")
232
233    # Nuernberg
234    bbox=(10.903198,49.560441,49.633534,11.038085)
235    render_tiles(bbox, mapfile, tile_dir, 10, 16, "Nuernberg")
236
237    # Karlsruhe
238    bbox=(8.179113,48.933617,8.489252,49.081707)
239    render_tiles(bbox, mapfile, tile_dir, 10, 16, "Karlsruhe")
240
241    # Karlsruhe+
242    bbox = (8.3,48.95,8.5,49.05)
243    render_tiles(bbox, mapfile, tile_dir, 1, 16, "Karlsruhe+")
244
245    # Augsburg
246    bbox = (8.3,48.95,8.5,49.05)
247    render_tiles(bbox, mapfile, tile_dir, 1, 16, "Augsburg")
248
249    # Augsburg+
250    bbox=(10.773251,48.369594,10.883834,48.438577)
251    render_tiles(bbox, mapfile, tile_dir, 10, 14, "Augsburg+")
252
253    # Europe+
254    bbox = (1.0,10.0, 20.6,50.0)
255    render_tiles(bbox, mapfile, tile_dir, 1, 11 , "Europe+")
Note: See TracBrowser for help on using the repository browser.