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

Last change on this file since 25604 was 25586, checked in by Dane Springmeyer, 9 years ago

exit script if user cancels instead of leaving queue hung with work

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