Advertisement
IgorPopov1234

generate_tiles.py

Dec 26th, 2011
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.78 KB | None | 0 0
  1. #!/usr/bin/env python
  2. from math import pi,cos,sin,log,exp,atan
  3. from subprocess import call
  4. import sys, os
  5. from Queue import Queue
  6.  
  7. import threading
  8.  
  9. try:
  10.     import mapnik2 as mapnik
  11. except:
  12.     import mapnik
  13.  
  14. DEG_TO_RAD = pi/180
  15. RAD_TO_DEG = 180/pi
  16.  
  17. # Default number of rendering threads to spawn, should be roughly equal to number of CPU cores available
  18. NUM_THREADS = 4
  19.  
  20.  
  21. def minmax (a,b,c):
  22.     a = max(a,b)
  23.     a = min(a,c)
  24.     return a
  25.  
  26. class 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.  
  57. class 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.  
  127. def 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.  
  192. if __name__ == "__main__":
  193.     home = os.environ['HOME']
  194.     try:
  195.         mapfile = os.environ['MAPNIK_MAP_FILE']
  196.     except KeyError:
  197.         mapfile = "osm.xml"
  198.     try:
  199.         tile_dir = os.environ['MAPNIK_TILE_DIR']
  200.     except KeyError:
  201.         tile_dir = "tiles/"
  202.  
  203.  
  204.     if not tile_dir.endswith('/'):
  205.         tile_dir = tile_dir + '/'
  206.  
  207.     if not os.path.exists(tile_dir):
  208.     os.makedirs(tile_dir)
  209.  
  210.     #-------------------------------------------------------------------------
  211.     #
  212.     # Change the following for different bounding boxes and zoom levels
  213.     #
  214.     # Start with an overview
  215.     # World
  216.     bbox = (-160.0,-80.0, 160.0,80.0)
  217.     render_tiles(bbox, mapfile, tile_dir, 0, 3, "World")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement