mapnikgenerate_tiles.py返回内存错误
我正在尝试使用 generate_tiles.py 创建图块,每次运行时脚本,我收到以下错误:
线程 Thread-2 中出现异常: 回溯(最近一次调用最后一次): 文件“/usr/lib/python2.7/threading.py”,第 552 行,位于 __bootstrap_inner 中 自我运行() 文件“/usr/lib/python2.7/threading.py”,第 505 行,运行中 self.__target(*self.__args, **self.__kwargs) 文件“render_tiles.py”,第 114 行,循环中 self.render_tile(tile_uri, x, y, z) 文件“render_tiles.py”,第 96 行,在 render_tile 中 mapnik.render(self.m, im) 内存错误
这是我的脚本。
#!/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 配置 这是shapefile。我从源代码编译了mapnik,所以它应该很好。可能是什么问题?
I am trying to create tiles using generate_tiles.py and every time I run the script, I get following error:
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
Here is my script.
#!/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)
Here is the mapnik config file
mapnik config
and here is the shapefile. I compiled mapnik from source , so it should be good. what could be the problem?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我可以看出它最初是
generate_tiles.py
,稍作修改。MemoryError 表示您正在请求渲染异常大的图像,或者您发现了 Mapnik bug。
由于前者更有可能,我认为您在修改
generate_tiles.py
时犯了一个错误。所以我拿了你的脚本,覆盖了 svn 中的最新版本,并找出了差异的差异: https://gist。 github.com/1197587。在第 #106 行和第 #115 行,您可能需要使用
==
而不是=
,因为否则您将生成非常大的 x 和 y 值,这些值是无效的,并且可能是导致非常大的图像请求(对于给定的图块来说这是错误的)。I can tell that is
generate_tiles.py
originally, modified slightly.A MemoryError indicates that you either are requesting an abnormally large image to be rendered or you have found a Mapnik bug.
Since the former is more likely I figure that you have made a mistake during modification of
generate_tiles.py
. So I took your script, overwrote the latest version in svn and got out a diff of the differences: https://gist.github.com/1197587.On line #106 and #115 you likely want to use
==
not=
because otherwise you are making very large x and y values that are invalid and may be the cause of very large image requests (that are wrong for a given tile).要生成的渲染线程的默认数量应大致等于可用 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(如果没有崩溃)。
Default number of rendering threads to spawn, should be roughly equal to number of CPU cores available
NUM_THREADS = 4
I had exactly the same error with NUM_THREADS=4.
Change the default value of NUM_THREADS to 1. I have an i5 (4 cores/1 thread per core) and NUM_THREADS=4 is very unstable. With NUM_THREADS=1 it works fine. If it works with NUM_THREADS=1 for you, try with NUM_THREADS=2 and NUM_THREADS=3 if nothing crashed.