2011-09-05 20 views
0

我試圖創建一個使用generate_tiles.py瓷磚和我每次運行腳本的時候,我得到以下錯誤:Mapnik的generate_tiles.py返回內存不足的錯誤

Exception in thread Thread-2: Traceback (most recent call last): File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner self.run() File "/usr/lib/python2.7/threading.py", line 505, in run self.__target(*self.__args, **self.__kwargs) File "render_tiles.py", line 114, in loop self.render_tile(tile_uri, x, y, z) File "render_tiles.py", line 96, in render_tile mapnik.render(self.m, im) MemoryError

這裏是我的腳本。

 
#!/usr/bin/python 
from math import pi,cos,sin,log,exp,atan 
from subprocess import call 
import sys, os 
from Queue import Queue 
import mapnik 
import threading 
import random 
import argparse 

custom_fonts_dir = '/usr/share/fonts/' 
mapnik.register_fonts(custom_fonts_dir) 

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[int(zoom)] 
     e = round(d[0] + float(ll[0]) * self.Bc[zoom]) 
     f = minmax(sin(DEG_TO_RAD * float(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) 
     # Obtain 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') 


    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): 
    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) 
     print "fromlattolon" 

     # 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 = 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 = 2**z): 
        continue 
       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) 
       queue.put(t) 

    # 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__": 
    MIN_LON = '29.5732'; 
    MAX_LON = '35.0360'; 
    MIN_LAT = '-1.4840'; 
    MAX_LAT = '4.2144'; 
    bbox = (MIN_LON, MIN_LAT,MAX_LON, MAX_LAT) 
    style_file="/home/mossplix/projects/Dev/mapit/map/static/tilemill/uganda_districts.xml" 
    tile_dir="/home/mossplix/projects/UnicefDev/mapit/map/static/tiles/" 
    min_zoom=7 
    max_zoom=14 
    render_tiles(bbox, style_file, tile_dir, min_zoom, max_zoom) 

這裏是Mapnik的配置文件 mapnik config 這裏是shapefile。我從源代碼編譯mapnik,所以它應該是好的。可能是什麼問題呢?

回答

3

我原來可以說是generate_tiles.py,稍加修改。

MemoryError指示您要求呈現異常大的圖像,或者您找到了Mapnik錯誤。

由於前者更有可能我認爲您在修改generate_tiles.py期間犯了一個錯誤。所以我把你的劇本寫在svn的最新版本中,並拿出了差異的差異:https://gist.github.com/1197587

在#106和#115行上,您可能想要使用==而不是=,因爲否則您會產生非常大的x和y值,這些值無效,可能是非常大的圖像請求的原因(對給定瓦)。渲染線程產卵的

+0

我認爲#106和#115這兩行是拼寫錯誤(實際上這個修飾符捕捉了它們)。錯誤仍然是

Exception in thread Thread-2: Traceback (most recent call last): File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner self.run() File "/usr/lib/python2.7/threading.py", line 505, in run self.__target(*self.__args, **self.__kwargs) File "render_tiles.py", line 114, in loop self.render_tile(tile_uri, x, y, z) File "render_tiles.py", line 96, in render_tile mapnik.render(self.m, im) MemoryError
mossplix

0

默認數目,應該大致等於CPU內核的數量的可用 NUM_THREADS = 4

我有正好與NUM_THREADS = 4同樣的錯誤。 將NUM_THREADS的默認值更改爲1.我有一個i5(每個核心有4個核心/ 1個線程),NUM_THREADS = 4非常不穩定。有了NUM_THREADS = 1,它工作正常。如果它適用於NUM_THREADS = 1,請嘗試NUM_THREADS = 2和NUM_THREADS = 3(如果沒有崩潰)。