From c82f301853e361a2156194b4cc14ab9c58f29f40 Mon Sep 17 00:00:00 2001 From: Darafei Praliaskouski Date: Wed, 27 Feb 2013 23:48:26 +0300 Subject: [PATCH] autopep8 --- src/generate_image.py | 10 +- src/gtk-app.py | 152 +++--- src/gtk_widget.py | 464 +++++++++--------- src/json_getter.py | 371 ++++++++------- src/libkomapnik.py | 562 +++++++++++----------- src/make_postgis_style.py | 32 +- src/osm2tiles.py | 286 +++++------ src/production.py | 8 +- src/render.py | 976 +++++++++++++++++++------------------- src/simple_wms.py | 17 +- src/style.py | 256 +++++----- src/twms_fetcher.py | 154 +++--- 12 files changed, 1643 insertions(+), 1645 deletions(-) diff --git a/src/generate_image.py b/src/generate_image.py index 4e2add3..b22f52d 100644 --- a/src/generate_image.py +++ b/src/generate_image.py @@ -27,7 +27,7 @@ from render import RasterTile svg = False if svg: - import cairo + import cairo style = MapCSS(1, 19) #zoom levels @@ -44,12 +44,12 @@ db = DataBackend() res = RasterTile(w, h, z, db) if svg: - file = open("test.svg", "wb") - res.surface = cairo.SVGSurface(file.name, w,h) + file = open("test.svg", "wb") + res.surface = cairo.SVGSurface(file.name, w,h) res.update_surface(bbox, z, style) if not svg: - res.surface.write_to_png("test.png") + res.surface.write_to_png("test.png") else: - res.surface.finish() \ No newline at end of file + res.surface.finish() diff --git a/src/gtk-app.py b/src/gtk-app.py index d93fa0f..3d3c131 100644 --- a/src/gtk-app.py +++ b/src/gtk-app.py @@ -35,91 +35,91 @@ from gtk_widget import KothicWidget try: - import psyco - psyco.full() + import psyco + psyco.full() except ImportError: - pass + pass # debug("Psyco import failed. Program may run slower. Ir you run it on i386 machine, please install Psyco to get best performance.") class KothicApp: - def __init__(self): - self.width, self.height = 800, 480 - - self.center_coord = (27.6549791, 53.8698) - self.zoom = 17. - self.data_projection = "EPSG:4326" - self.data = DataBackend() - self.load_style() - - - self.request_d = (0,0) - self.window = gtk.Window() - + def __init__(self): + self.width, self.height = 800, 480 + + self.center_coord = (27.6549791, 53.8698) + self.zoom = 17. + self.data_projection = "EPSG:4326" + self.data = DataBackend() + self.load_style() - self.window.set_size_request(self.width, self.height) - - self.window.connect("destroy", gtk.main_quit) - - self.window.set_title("Kothic renderer") - menu = gtk.MenuBar() - - filemenu = gtk.Menu() - filem = gtk.MenuItem("File") - filem.set_submenu(filemenu) - i = gtk.MenuItem("Reload style") - i.connect("activate", self.load_style) - filemenu.append(i) - - stylemenu = gtk.Menu() - stylem = gtk.MenuItem("Style") - stylem.set_submenu(stylemenu) - styles = [name for name in os.listdir("styles") if ".mapcss" in name] - for style in styles: - i = gtk.MenuItem(style) - i.StyleName = style - i.connect("activate", self.reload_style) - stylemenu.append(i) - - i = gtk.MenuItem("Exit") - i.connect("activate", gtk.main_quit) - filemenu.append(i) - - menu.append(filem) - menu.append(stylem) - - vbox = gtk.VBox(False, 2) - vbox.pack_start(menu,False,False,0) - - self.KothicWidget = KothicWidget(self.data, self.style) - self.KothicWidget.set_zoom(self.zoom) - self.KothicWidget.jump_to(self.center_coord) - - - - vbox.pack_end(self.KothicWidget) - - - self.window.add(vbox) - - def load_style(self): - self.style = Styling(0,25) - self.style.parse(open("styles/osmosnimki-maps.mapcss","r").read()) - def reload_style(self,w): - self.style = Styling(0,25) - self.style.parse(open("styles/%s"%w.StyleName,"r").read()) - self.KothicWidget.style_backend = self.style - self.KothicWidget.redraw() + self.request_d = (0,0) + self.window = gtk.Window() - def main(self): - self.window.show_all() - gtk.main() - exit() + self.window.set_size_request(self.width, self.height) + + self.window.connect("destroy", gtk.main_quit) + + self.window.set_title("Kothic renderer") + menu = gtk.MenuBar() + + filemenu = gtk.Menu() + filem = gtk.MenuItem("File") + filem.set_submenu(filemenu) + i = gtk.MenuItem("Reload style") + i.connect("activate", self.load_style) + filemenu.append(i) + + stylemenu = gtk.Menu() + stylem = gtk.MenuItem("Style") + stylem.set_submenu(stylemenu) + styles = [name for name in os.listdir("styles") if ".mapcss" in name] + for style in styles: + i = gtk.MenuItem(style) + i.StyleName = style + i.connect("activate", self.reload_style) + stylemenu.append(i) + + i = gtk.MenuItem("Exit") + i.connect("activate", gtk.main_quit) + filemenu.append(i) + + menu.append(filem) + menu.append(stylem) + + vbox = gtk.VBox(False, 2) + vbox.pack_start(menu,False,False,0) + + self.KothicWidget = KothicWidget(self.data, self.style) + self.KothicWidget.set_zoom(self.zoom) + self.KothicWidget.jump_to(self.center_coord) + + + + vbox.pack_end(self.KothicWidget) + + + self.window.add(vbox) + + def load_style(self): + self.style = Styling(0,25) + self.style.parse(open("styles/osmosnimki-maps.mapcss","r").read()) + def reload_style(self,w): + self.style = Styling(0,25) + self.style.parse(open("styles/%s"%w.StyleName,"r").read()) + self.KothicWidget.style_backend = self.style + self.KothicWidget.redraw() + + + def main(self): + + self.window.show_all() + gtk.main() + exit() if __name__ == "__main__": - gtk.gdk.threads_init() - kap = KothicApp() - kap.main() + gtk.gdk.threads_init() + kap = KothicApp() + kap.main() diff --git a/src/gtk_widget.py b/src/gtk_widget.py index 929e5ca..14d134e 100644 --- a/src/gtk_widget.py +++ b/src/gtk_widget.py @@ -32,256 +32,256 @@ import twms.bbox from twms import projections class KothicWidget(gtk.DrawingArea): - def __init__(self, data, style): - gtk.DrawingArea.__init__(self) - self.data_backend = data - self.style_backend = style - self.request_d = (0,0) - self.tiles = TileSource(data,style, callback=self.redraw) - self.dx = 0 - self.dy = 0 - self.drag_x = 0 - self.drag_y = 0 - self.drag = False - self.rastertile = None - self.f = True - self.width = 0 - self.height = 0 - self.max_zoom = 25 + def __init__(self, data, style): + gtk.DrawingArea.__init__(self) + self.data_backend = data + self.style_backend = style + self.request_d = (0,0) + self.tiles = TileSource(data,style, callback=self.redraw) + self.dx = 0 + self.dy = 0 + self.drag_x = 0 + self.drag_y = 0 + self.drag = False + self.rastertile = None + self.f = True + self.width = 0 + self.height = 0 + self.max_zoom = 25 - self.zoom = 0 - self.center_coord = (0.0,0.0) - self.old_zoom = 1 - self.old_center_coord = (0.0,0.1) - self.tilebox = [] # bbox of currently seen tiles - self.bbox = [] - - - self.add_events(gtk.gdk.BUTTON1_MOTION_MASK) - self.add_events(gtk.gdk.POINTER_MOTION_MASK) - self.add_events(gtk.gdk.BUTTON_PRESS_MASK) - self.add_events(gtk.gdk.BUTTON_RELEASE_MASK) - self.add_events(gtk.gdk.SCROLL) + self.zoom = 0 + self.center_coord = (0.0,0.0) + self.old_zoom = 1 + self.old_center_coord = (0.0,0.1) + self.tilebox = [] # bbox of currently seen tiles + self.bbox = [] + + + self.add_events(gtk.gdk.BUTTON1_MOTION_MASK) + self.add_events(gtk.gdk.POINTER_MOTION_MASK) + self.add_events(gtk.gdk.BUTTON_PRESS_MASK) + self.add_events(gtk.gdk.BUTTON_RELEASE_MASK) + self.add_events(gtk.gdk.SCROLL) # self.window.add_events(gtk.gdk.BUTTON1_MOTION_MASK) - self.connect("expose_event",self.expose_ev) - self.connect("motion_notify_event",self.motion_ev) - self.connect("button_press_event",self.press_ev) - self.connect("button_release_event",self.release_ev) - self.connect("scroll_event",self.scroll_ev) - + self.connect("expose_event",self.expose_ev) + self.connect("motion_notify_event",self.motion_ev) + self.connect("button_press_event",self.press_ev) + self.connect("button_release_event",self.release_ev) + self.connect("scroll_event",self.scroll_ev) + # self.surface = cairo.ImageSurfaceicreate(gtk.RGB24, self.width, self.height) - def set_zoom(self, zoom): - self.zoom = zoom - self.queue_draw() - def jump_to(self, lonlat): - self.center_coord = lonlat - self.queue_draw() - def zoom_to(self, bbox): - self.zoom = twms.bbox.zoom_for_bbox (bbox, (self.width,self.height), {"proj":"EPSG:3857","max_zoom":self.max_zoom})-1 - print "Zoom:", self.zoom - self.center_coord = ((bbox[0]+bbox[2])/2,(bbox[1]+bbox[3])/2) - print self.center_coord - self.redraw() + def set_zoom(self, zoom): + self.zoom = zoom + self.queue_draw() + def jump_to(self, lonlat): + self.center_coord = lonlat + self.queue_draw() + def zoom_to(self, bbox): + self.zoom = twms.bbox.zoom_for_bbox (bbox, (self.width,self.height), {"proj":"EPSG:3857","max_zoom":self.max_zoom})-1 + print "Zoom:", self.zoom + self.center_coord = ((bbox[0]+bbox[2])/2,(bbox[1]+bbox[3])/2) + print self.center_coord + self.redraw() - - def motion_ev(self, widget, event): - if self.drag: - self.dx = event.x - self.drag_x - self.dy = event.y - self.drag_y - #if((abs(self.dx) > 3 or abs(self.dy) > 3) and self.f): - if True: - # x = event.x - # y = event.y - # lo1, la1, lo2, la2 = self.tilebox - # self.center_coord = projections.coords_by_tile(self.zoom,1.*x/self.width*(lo2-lo1)+lo1, la1+(1.*y/(self.height)*(la2-la1)),"EPSG:3857") + def motion_ev(self, widget, event): + + if self.drag: + self.dx = event.x - self.drag_x + self.dy = event.y - self.drag_y + #if((abs(self.dx) > 3 or abs(self.dy) > 3) and self.f): + if True: + # x = event.x + # y = event.y + # lo1, la1, lo2, la2 = self.tilebox + # self.center_coord = projections.coords_by_tile(self.zoom,1.*x/self.width*(lo2-lo1)+lo1, la1+(1.*y/(self.height)*(la2-la1)),"EPSG:3857") + widget.queue_draw() + def press_ev(self, widget, event): + if event.button == 1: + #debug("Start drag") + self.drag = True + self.drag_x = event.x + self.drag_y = event.y + self.timer = Timer("Drag") + #elif event.button == 2: + #debug("Button2") + #elif event.button == 3: + #debug("Button3") + def release_ev(self, widget, event): + if event.button == 1: + #debug("Stop drag") + self.drag = False + self.timer.stop() + #debug("dd: %s,%s "%(self.dx, self.dy)) + x = event.x + y = event.y + lo1, la1, lo2, la2 = projections.from4326(self.bbox, "EPSG:3857") + print lo1, la1, lo2, la2 + #self.center_coord = projections.to4326((0.5*(self.width+self.dx)/self.width*(lo1-lo2)+lo2, la1+(0.5*(self.height+self.dy)/self.height*(la2-la1))),"EPSG:3857") + + self.center_coord = projections.to4326((0.5*(self.width+2*self.dx)/self.width*(lo1-lo2)+lo2, la1+(0.5*(self.height+2*self.dy)/self.height*(la2-la1))),"EPSG:3857") + #self.rastertile.screen2lonlat(self.rastertile.w/2 - self.dx, self.rastertile.h/2 - self.dy); + self.dx = 0 + self.dy = 0 + self.redraw() + + def scroll_ev(self, widget, event): + if event.direction == gtk.gdk.SCROLL_UP: + if self.zoom+0.5 <= self.max_zoom: + self.zoom += 0.5 + #debug("Zoom in") + elif event.direction == gtk.gdk.SCROLL_DOWN: + if self.zoom >= 0: ## negative zooms are nonsense + self.zoom -= 0.5 + # debug("Zoom out") + #self.redraw() + debug("new zoom: %s"%(self.zoom)) widget.queue_draw() - def press_ev(self, widget, event): - if event.button == 1: - #debug("Start drag") - self.drag = True - self.drag_x = event.x - self.drag_y = event.y - self.timer = Timer("Drag") - #elif event.button == 2: - #debug("Button2") - #elif event.button == 3: - #debug("Button3") - def release_ev(self, widget, event): - if event.button == 1: - #debug("Stop drag") - self.drag = False - self.timer.stop() - #debug("dd: %s,%s "%(self.dx, self.dy)) - x = event.x - y = event.y - lo1, la1, lo2, la2 = projections.from4326(self.bbox, "EPSG:3857") - print lo1, la1, lo2, la2 - #self.center_coord = projections.to4326((0.5*(self.width+self.dx)/self.width*(lo1-lo2)+lo2, la1+(0.5*(self.height+self.dy)/self.height*(la2-la1))),"EPSG:3857") - - self.center_coord = projections.to4326((0.5*(self.width+2*self.dx)/self.width*(lo1-lo2)+lo2, la1+(0.5*(self.height+2*self.dy)/self.height*(la2-la1))),"EPSG:3857") - #self.rastertile.screen2lonlat(self.rastertile.w/2 - self.dx, self.rastertile.h/2 - self.dy); - self.dx = 0 - self.dy = 0 - self.redraw() - - def scroll_ev(self, widget, event): - if event.direction == gtk.gdk.SCROLL_UP: - if self.zoom+0.5 <= self.max_zoom: - self.zoom += 0.5 - #debug("Zoom in") - elif event.direction == gtk.gdk.SCROLL_DOWN: - if self.zoom >= 0: ## negative zooms are nonsense - self.zoom -= 0.5 - # debug("Zoom out") - #self.redraw() - debug("new zoom: %s"%(self.zoom)) - widget.queue_draw() - def redraw(self): - """ - Force screen redraw. - """ - #res = RasterTile(3*self.width, 3*self.height, self.zoom, self.data_backend) - #res.update_surface_by_center(self.center_coord, self.zoom, self.style_backend) - #self.rastertile = res - self.queue_draw() + def redraw(self): + """ + Force screen redraw. + """ + #res = RasterTile(3*self.width, 3*self.height, self.zoom, self.data_backend) + #res.update_surface_by_center(self.center_coord, self.zoom, self.style_backend) + #self.rastertile = res + self.queue_draw() - def expose_ev(self, widget, event): - if(widget.allocation.width != self.width or widget.allocation.height != self.height ): - #debug("Rrresize!") - self.width = widget.allocation.width - self.height = widget.allocation.height + def expose_ev(self, widget, event): + if(widget.allocation.width != self.width or widget.allocation.height != self.height ): + #debug("Rrresize!") + self.width = widget.allocation.width + self.height = widget.allocation.height - cr = widget.window.cairo_create() - if self.old_center_coord != self.center_coord or self.old_zoom != self.zoom: - #print "Recentered!" - xy = projections.from4326(self.center_coord,"EPSG:3857") - xy1 = projections.to4326((xy[0]-40075016.*(0.5**(self.zoom))/self.tiles.tilewidth*self.width, xy[1]-40075016.*(0.5**(self.zoom))/self.tiles.tileheight*self.height), "EPSG:3857") - xy2 = projections.to4326((xy[0]+40075016.*(0.5**(self.zoom))/self.tiles.tilewidth*self.width, xy[1]+40075016.*(0.5**(self.zoom))/self.tiles.tileheight*self.height), "EPSG:3857") - self.bbox = (xy1[0],xy1[1],xy2[0],xy2[1]) - self.tilebox = projections.tile_by_bbox(self.bbox, self.zoom, "EPSG:3857") - self.old_center_coord = self.center_coord - self.old_zoom = self.zoom - from_tile_x, from_tile_y, to_tile_x, to_tile_y = self.tilebox - dx = 1.*(from_tile_x - int(from_tile_x))*self.tiles.tilewidth - dy = 1.*(from_tile_y - int(from_tile_y))*self.tiles.tileheight - print dx,dy - #print self.dx, self.dy - onscreen_tiles = set() - for x in range (int(from_tile_x), int(to_tile_x)+1): - for y in range (int(to_tile_y), int(from_tile_y)+1): - onscreen_tiles.add((self.zoom,x,y)) - self.tiles.onscreen = onscreen_tiles - for z,x,y in onscreen_tiles: - tile = self.tiles[(self.zoom,x,y)] - #print dx+(x-from_tile_x)*self.tiles.tilewidth-self.width - #print dy+(y-from_tile_y)*self.tiles.tileheight-self.height - #cr.set_source_surface(tile, int(self.dx-dx+(x-int(from_tile_x))*self.tiles.tilewidth-self.width), int(self.dy-dy-(int(from_tile_y)-y)*self.tiles.tileheight+self.height)) - cr.set_source_surface(tile, int(self.dx-dx+(x-int(from_tile_x))*self.tiles.tilewidth), int(self.dy-dy-(int(from_tile_y)-y)*self.tiles.tileheight+self.height)) - cr.paint() - #cr.set_source_surface(self.rastertile.surface, self.dx-self.width + self.rastertile.offset_x, self.dy - self.height + self.rastertile.offset_y) - - #self.comm[3].release() + cr = widget.window.cairo_create() + if self.old_center_coord != self.center_coord or self.old_zoom != self.zoom: + #print "Recentered!" + xy = projections.from4326(self.center_coord,"EPSG:3857") + xy1 = projections.to4326((xy[0]-40075016.*(0.5**(self.zoom))/self.tiles.tilewidth*self.width, xy[1]-40075016.*(0.5**(self.zoom))/self.tiles.tileheight*self.height), "EPSG:3857") + xy2 = projections.to4326((xy[0]+40075016.*(0.5**(self.zoom))/self.tiles.tilewidth*self.width, xy[1]+40075016.*(0.5**(self.zoom))/self.tiles.tileheight*self.height), "EPSG:3857") + self.bbox = (xy1[0],xy1[1],xy2[0],xy2[1]) + self.tilebox = projections.tile_by_bbox(self.bbox, self.zoom, "EPSG:3857") + self.old_center_coord = self.center_coord + self.old_zoom = self.zoom + from_tile_x, from_tile_y, to_tile_x, to_tile_y = self.tilebox + dx = 1.*(from_tile_x - int(from_tile_x))*self.tiles.tilewidth + dy = 1.*(from_tile_y - int(from_tile_y))*self.tiles.tileheight + print dx,dy + #print self.dx, self.dy + onscreen_tiles = set() + for x in range (int(from_tile_x), int(to_tile_x)+1): + for y in range (int(to_tile_y), int(from_tile_y)+1): + onscreen_tiles.add((self.zoom,x,y)) + self.tiles.onscreen = onscreen_tiles + for z,x,y in onscreen_tiles: + tile = self.tiles[(self.zoom,x,y)] + #print dx+(x-from_tile_x)*self.tiles.tilewidth-self.width + #print dy+(y-from_tile_y)*self.tiles.tileheight-self.height + #cr.set_source_surface(tile, int(self.dx-dx+(x-int(from_tile_x))*self.tiles.tilewidth-self.width), int(self.dy-dy-(int(from_tile_y)-y)*self.tiles.tileheight+self.height)) + cr.set_source_surface(tile, int(self.dx-dx+(x-int(from_tile_x))*self.tiles.tilewidth), int(self.dy-dy-(int(from_tile_y)-y)*self.tiles.tileheight+self.height)) + cr.paint() + #cr.set_source_surface(self.rastertile.surface, self.dx-self.width + self.rastertile.offset_x, self.dy - self.height + self.rastertile.offset_y) + + #self.comm[3].release() class TileSource: - def __init__(self,data,style, callback = lambda: None): - self.tiles = {} - self.tilewidth = 2048 - self.tileheight = 2048 - self.max_tiles = 32 - self.data_backend = data - self.style_backend = style - self.callback = callback - self.onscreen = set() - self._singlethread = False - self._prerender = True - def __getitem__(self,(z,x,y),wait=False): + def __init__(self,data,style, callback = lambda: None): + self.tiles = {} + self.tilewidth = 2048 + self.tileheight = 2048 + self.max_tiles = 32 + self.data_backend = data + self.style_backend = style + self.callback = callback + self.onscreen = set() + self._singlethread = False + self._prerender = True + def __getitem__(self,(z,x,y),wait=False): - try: - #if "surface" in self.tiles[(z,x,y)] and not wait: - # self._callback((z,x,y), True) - print "Tiles count:", len(self.tiles) - return self.tiles[(z,x,y)]["surface"] - except: - self.tiles[(z,x,y)] = {"tile": RasterTile(self.tilewidth, self.tileheight, z, self.data_backend)} - self.tiles[(z,x,y)]["start_time"] = datetime.datetime.now() - if self._singlethread: - - self.tiles[(z,x,y)]["surface"] = self.tiles[(z,x,y)]["tile"].surface - self.tiles[(z,x,y)]["tile"].update_surface(projections.bbox_by_tile(z,x,y,"EPSG:3857"), z, self.style_backend, lambda p=False: self._callback((z,x,y),p)) - - del self.tiles[(z,x,y)]["tile"] - else: - self.tiles[(z,x,y)]["surface"] = self.tiles[(z,x,y)]["tile"].surface.create_similar(cairo.CONTENT_COLOR_ALPHA, self.tilewidth, self.tileheight) - self.tiles[(z,x,y)]["thread"] = threading.Thread(None, self.tiles[(z,x,y)]["tile"].update_surface,None, (projections.bbox_by_tile(z,x,y,"EPSG:3857"), z, self.style_backend, lambda p=False: self._callback((z,x,y),p))) - self.tiles[(z,x,y)]["thread"].start() - if wait: - self.tiles[(z,x,y)]["thread"].join() - return self.tiles[(z,x,y)]["surface"] - def _callback (self, (z,x,y),last): - #if last: - # print last, "dddddddddddddddddd" - if not self._singlethread: - if ((z,x,y) in self.onscreen or last) and "tile" in self.tiles[(z,x,y)]: - cr = cairo.Context(self.tiles[(z,x,y)]["surface"]) - cr.set_source_surface(self.tiles[(z,x,y)]["tile"].surface,0,0) - cr.paint() - - if last: try: - del self.tiles[(z,x,y)]["thread"] - del self.tiles[(z,x,y)]["tile"] - except KeyError: - pass - self.tiles[(z,x,y)]["finish_time"] = datetime.datetime.now() - self.tiles[(z,x,y)]["start_time"] - gobject.idle_add(self.callback) - self.collect_grabage() - if last and self._prerender: - if (z,x,y) in self.onscreen: - a = self.__getitem__((z-1,x/2,y/2),True) - if (z,x,y) in self.onscreen: - a = self.__getitem__((z+1,x*2,y*2),True) - if (z,x,y) in self.onscreen: - a = self.__getitem__((z+1,x*2+1,y*2),True) - if (z,x,y) in self.onscreen: - a = self.__getitem__((z+1,x*2,y*2+1),True) - if (z,x,y) in self.onscreen: - a = self.__getitem__((z+1,x*2+1,y*2+1),True) - if (z,x,y) in self.onscreen: - a = self.__getitem__((z,x+1,y),True) - if (z,x,y) in self.onscreen: - a = self.__getitem__((z,x,y+1),True) - if (z,x,y) in self.onscreen: - a = self.__getitem__((z,x-1,y),True) - if (z,x,y) in self.onscreen: - a = self.__getitem__((z,x,y-1),True) - def collect_grabage (self): - if len(self.tiles)> self.max_tiles: - # let's kick out the fastest rendered tiles - it's easy to rerender those - # don't touch onscreen tiles - cand = set(self.tiles.keys()) - cand.difference_update(self.onscreen) - cand = [i for i in cand if "finish_time" in self.tiles[i]] - cand.sort(lambda i,j: self.tiles[i]["finish_time"] self.max_tiles): - c = cand.pop() - try: - print "Killed tile ", c, " - finished in ",str(self.tiles[c]["finish_time"]), ", ago:", str(datetime.datetime.now()-self.tiles[c]["start_time"]) - del self.tiles[c] - except KeyError: - pass - else: - break + #if "surface" in self.tiles[(z,x,y)] and not wait: + # self._callback((z,x,y), True) + print "Tiles count:", len(self.tiles) + return self.tiles[(z,x,y)]["surface"] + except: + self.tiles[(z,x,y)] = {"tile": RasterTile(self.tilewidth, self.tileheight, z, self.data_backend)} + self.tiles[(z,x,y)]["start_time"] = datetime.datetime.now() + if self._singlethread: + + self.tiles[(z,x,y)]["surface"] = self.tiles[(z,x,y)]["tile"].surface + self.tiles[(z,x,y)]["tile"].update_surface(projections.bbox_by_tile(z,x,y,"EPSG:3857"), z, self.style_backend, lambda p=False: self._callback((z,x,y),p)) + + del self.tiles[(z,x,y)]["tile"] + else: + self.tiles[(z,x,y)]["surface"] = self.tiles[(z,x,y)]["tile"].surface.create_similar(cairo.CONTENT_COLOR_ALPHA, self.tilewidth, self.tileheight) + self.tiles[(z,x,y)]["thread"] = threading.Thread(None, self.tiles[(z,x,y)]["tile"].update_surface,None, (projections.bbox_by_tile(z,x,y,"EPSG:3857"), z, self.style_backend, lambda p=False: self._callback((z,x,y),p))) + self.tiles[(z,x,y)]["thread"].start() + if wait: + self.tiles[(z,x,y)]["thread"].join() + return self.tiles[(z,x,y)]["surface"] + def _callback (self, (z,x,y),last): + #if last: + # print last, "dddddddddddddddddd" + if not self._singlethread: + if ((z,x,y) in self.onscreen or last) and "tile" in self.tiles[(z,x,y)]: + cr = cairo.Context(self.tiles[(z,x,y)]["surface"]) + cr.set_source_surface(self.tiles[(z,x,y)]["tile"].surface,0,0) + cr.paint() + + if last: + try: + del self.tiles[(z,x,y)]["thread"] + del self.tiles[(z,x,y)]["tile"] + except KeyError: + pass + self.tiles[(z,x,y)]["finish_time"] = datetime.datetime.now() - self.tiles[(z,x,y)]["start_time"] + gobject.idle_add(self.callback) + self.collect_grabage() + if last and self._prerender: + if (z,x,y) in self.onscreen: + a = self.__getitem__((z-1,x/2,y/2),True) + if (z,x,y) in self.onscreen: + a = self.__getitem__((z+1,x*2,y*2),True) + if (z,x,y) in self.onscreen: + a = self.__getitem__((z+1,x*2+1,y*2),True) + if (z,x,y) in self.onscreen: + a = self.__getitem__((z+1,x*2,y*2+1),True) + if (z,x,y) in self.onscreen: + a = self.__getitem__((z+1,x*2+1,y*2+1),True) + if (z,x,y) in self.onscreen: + a = self.__getitem__((z,x+1,y),True) + if (z,x,y) in self.onscreen: + a = self.__getitem__((z,x,y+1),True) + if (z,x,y) in self.onscreen: + a = self.__getitem__((z,x-1,y),True) + if (z,x,y) in self.onscreen: + a = self.__getitem__((z,x,y-1),True) + def collect_grabage (self): + if len(self.tiles)> self.max_tiles: + # let's kick out the fastest rendered tiles - it's easy to rerender those + # don't touch onscreen tiles + cand = set(self.tiles.keys()) + cand.difference_update(self.onscreen) + cand = [i for i in cand if "finish_time" in self.tiles[i]] + cand.sort(lambda i,j: self.tiles[i]["finish_time"] self.max_tiles): + c = cand.pop() + try: + print "Killed tile ", c, " - finished in ",str(self.tiles[c]["finish_time"]), ", ago:", str(datetime.datetime.now()-self.tiles[c]["start_time"]) + del self.tiles[c] + except KeyError: + pass + else: + break if __name__ == "__main__": - gtk.gdk.threads_init() - kap = KothicApp() - kap.main() + gtk.gdk.threads_init() + kap = KothicApp() + kap.main() diff --git a/src/json_getter.py b/src/json_getter.py index 5e31dac..40f5c59 100644 --- a/src/json_getter.py +++ b/src/json_getter.py @@ -11,189 +11,189 @@ reload(sys) sys.setdefaultencoding("utf-8") # a hack to support UTF-8 try: - import psyco - psyco.full() + import psyco + psyco.full() except ImportError: - pass - #print >>sys.stderr, "Psyco import failed. Program may run slower. If you run it on i386 machine, please install Psyco to get best performance." + pass + #print >>sys.stderr, "Psyco import failed. Program may run slower. If you run it on i386 machine, please install Psyco to get best performance." def get_vectors(bbox, zoom, style, vec = "polygon"): - bbox_p = projections.from4326(bbox, "EPSG:3857") - geomcolumn = "way" + bbox_p = projections.from4326(bbox, "EPSG:3857") + geomcolumn = "way" - database = "dbname=gis user=gis" - pxtolerance = 1.8 - intscalefactor = 10000 - ignore_columns = set(["way_area", "osm_id", geomcolumn, "tags", "z_order"]) - table = {"polygon":"planet_osm_polygon", "line":"planet_osm_line","point":"planet_osm_point", "coastline": "coastlines"} + database = "dbname=gis user=gis" + pxtolerance = 1.8 + intscalefactor = 10000 + ignore_columns = set(["way_area", "osm_id", geomcolumn, "tags", "z_order"]) + table = {"polygon":"planet_osm_polygon", "line":"planet_osm_line","point":"planet_osm_point", "coastline": "coastlines"} - a = psycopg2.connect(database) - b = a.cursor() - if vec != "coastline": - b.execute("SELECT * FROM %s LIMIT 1;" % table[vec]) + a = psycopg2.connect(database) + b = a.cursor() + if vec != "coastline": + b.execute("SELECT * FROM %s LIMIT 1;" % table[vec]) + names = [q[0] for q in b.description] + for i in ignore_columns: + if i in names: + names.remove(i) + names = ",".join(['"'+i+'"' for i in names]) + + + taghint = "*" + types = {"line":"line","polygon":"area", "point":"node"} + adp = "" + if "get_sql_hints" in dir(style): + sql_hint = style.get_sql_hints(types[vec], zoom) + adp = [] + for tp in sql_hint: + add = [] + for j in tp[0]: + if j not in names: + break + else: + add.append(tp[1]) + if add: + add = " OR ".join(add) + add = "("+add+")" + adp.append(add) + adp = " OR ".join(adp) + if adp: + adp = adp.replace("<", "<") + adp = adp.replace(">", ">") + + + if vec == "polygon": + query = """select ST_AsGeoJSON(ST_TransScale(ST_ForceRHR(ST_Intersection(way,SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913))),%s,%s,%s,%s),0) as %s, + ST_AsGeoJSON(ST_TransScale(ST_ForceRHR(ST_PointOnSurface(way)),%s,%s,%s,%s),0) as reprpoint, %s from + (select (ST_Dump(ST_Multi(ST_SimplifyPreserveTopology(ST_Buffer(way,-%s),%s)))).geom as %s, %s from + (select ST_Union(way) as %s, %s from + (select ST_Buffer(way, %s) as %s, %s from + %s + where (%s) + and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) + and way_area > %s + ) p + group by %s + ) p + where ST_Area(way) > %s + order by ST_Area(way) + ) p + """%(bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], + -bbox_p[0],-bbox_p[1],intscalefactor/(bbox_p[2]-bbox_p[0]),intscalefactor/(bbox_p[3]-bbox_p[1]), + geomcolumn, + -bbox_p[0],-bbox_p[1],intscalefactor/(bbox_p[2]-bbox_p[0]),intscalefactor/(bbox_p[3]-bbox_p[1]), + names, + pixel_size_at_zoom(zoom, pxtolerance),pixel_size_at_zoom(zoom, pxtolerance), + geomcolumn, names, + geomcolumn, names, + pixel_size_at_zoom(zoom, pxtolerance), + geomcolumn, names, + table[vec], + adp, + bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], + (pixel_size_at_zoom(zoom, pxtolerance)**2)/pxtolerance, + names, + pixel_size_at_zoom(zoom, pxtolerance)**2 + ) + elif vec == "line": + query = """select ST_AsGeoJSON(ST_TransScale(ST_Intersection(way,SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913)),%s,%s,%s,%s),0) as %s, %s from + (select (ST_Dump(ST_Multi(ST_SimplifyPreserveTopology(ST_LineMerge(way),%s)))).geom as %s, %s from + (select ST_Union(way) as %s, %s from + %s + where (%s) + and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) + + group by %s + ) p + + ) p + """%(bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], + -bbox_p[0],-bbox_p[1],intscalefactor/(bbox_p[2]-bbox_p[0]),intscalefactor/(bbox_p[3]-bbox_p[1]), + geomcolumn, names, + pixel_size_at_zoom(zoom, pxtolerance), + geomcolumn, names, + geomcolumn, names, + table[vec], + adp, + bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], + + names, + + ) + elif vec == "point": + query = """select ST_AsGeoJSON(ST_TransScale(way,%s,%s,%s,%s),0) as %s, %s + from %s where + (%s) + and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) + limit 10000 + """%( + -bbox_p[0],-bbox_p[1],intscalefactor/(bbox_p[2]-bbox_p[0]),intscalefactor/(bbox_p[3]-bbox_p[1]), + geomcolumn, names, + table[vec], + adp, + bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], + + ) + elif vec == "coastline": + query = """select ST_AsGeoJSON(ST_TransScale(ST_ForceRHR(ST_Intersection(way,SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913))),%s,%s,%s,%s),0) as %s, 'coastline' as "natural" from + (select (ST_Dump(ST_Multi(ST_SimplifyPreserveTopology(ST_Buffer(way,-%s),%s)))).geom as %s from + (select ST_Union(way) as %s from + (select ST_Buffer(SetSRID(the_geom,900913), %s) as %s from + %s + where + SetSRID(the_geom,900913) && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) + ) p + ) p + where ST_Area(way) > %s + ) p + """%(bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], + -bbox_p[0],-bbox_p[1],intscalefactor/(bbox_p[2]-bbox_p[0]),intscalefactor/(bbox_p[3]-bbox_p[1]), + geomcolumn, + pixel_size_at_zoom(zoom, pxtolerance),pixel_size_at_zoom(zoom, pxtolerance), + geomcolumn, + geomcolumn, + pixel_size_at_zoom(zoom, pxtolerance), + geomcolumn, + table[vec], + bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], + pixel_size_at_zoom(zoom, pxtolerance)**2 + ) + #print query + a = psycopg2.connect(database) + b = a.cursor() + b.execute(query) names = [q[0] for q in b.description] - for i in ignore_columns: - if i in names: - names.remove(i) - names = ",".join(['"'+i+'"' for i in names]) + ROWS_FETCHED = 0 + polygons = [] - taghint = "*" - types = {"line":"line","polygon":"area", "point":"node"} - adp = "" - if "get_sql_hints" in dir(style): - sql_hint = style.get_sql_hints(types[vec], zoom) - adp = [] - for tp in sql_hint: - add = [] - for j in tp[0]: - if j not in names: - break - else: - add.append(tp[1]) - if add: - add = " OR ".join(add) - add = "("+add+")" - adp.append(add) - adp = " OR ".join(adp) - if adp: - adp = adp.replace("<", "<") - adp = adp.replace(">", ">") - - - if vec == "polygon": - query = """select ST_AsGeoJSON(ST_TransScale(ST_ForceRHR(ST_Intersection(way,SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913))),%s,%s,%s,%s),0) as %s, - ST_AsGeoJSON(ST_TransScale(ST_ForceRHR(ST_PointOnSurface(way)),%s,%s,%s,%s),0) as reprpoint, %s from - (select (ST_Dump(ST_Multi(ST_SimplifyPreserveTopology(ST_Buffer(way,-%s),%s)))).geom as %s, %s from - (select ST_Union(way) as %s, %s from - (select ST_Buffer(way, %s) as %s, %s from - %s - where (%s) - and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) - and way_area > %s - ) p - group by %s - ) p - where ST_Area(way) > %s - order by ST_Area(way) - ) p - """%(bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], - -bbox_p[0],-bbox_p[1],intscalefactor/(bbox_p[2]-bbox_p[0]),intscalefactor/(bbox_p[3]-bbox_p[1]), - geomcolumn, - -bbox_p[0],-bbox_p[1],intscalefactor/(bbox_p[2]-bbox_p[0]),intscalefactor/(bbox_p[3]-bbox_p[1]), - names, - pixel_size_at_zoom(zoom, pxtolerance),pixel_size_at_zoom(zoom, pxtolerance), - geomcolumn, names, - geomcolumn, names, - pixel_size_at_zoom(zoom, pxtolerance), - geomcolumn, names, - table[vec], - adp, - bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], - (pixel_size_at_zoom(zoom, pxtolerance)**2)/pxtolerance, - names, - pixel_size_at_zoom(zoom, pxtolerance)**2 - ) - elif vec == "line": - query = """select ST_AsGeoJSON(ST_TransScale(ST_Intersection(way,SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913)),%s,%s,%s,%s),0) as %s, %s from - (select (ST_Dump(ST_Multi(ST_SimplifyPreserveTopology(ST_LineMerge(way),%s)))).geom as %s, %s from - (select ST_Union(way) as %s, %s from - %s - where (%s) - and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) - - group by %s - ) p - - ) p - """%(bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], - -bbox_p[0],-bbox_p[1],intscalefactor/(bbox_p[2]-bbox_p[0]),intscalefactor/(bbox_p[3]-bbox_p[1]), - geomcolumn, names, - pixel_size_at_zoom(zoom, pxtolerance), - geomcolumn, names, - geomcolumn, names, - table[vec], - adp, - bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], - - names, - - ) - elif vec == "point": - query = """select ST_AsGeoJSON(ST_TransScale(way,%s,%s,%s,%s),0) as %s, %s - from %s where - (%s) - and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) - limit 10000 - """%( - -bbox_p[0],-bbox_p[1],intscalefactor/(bbox_p[2]-bbox_p[0]),intscalefactor/(bbox_p[3]-bbox_p[1]), - geomcolumn, names, - table[vec], - adp, - bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], - - ) - elif vec == "coastline": - query = """select ST_AsGeoJSON(ST_TransScale(ST_ForceRHR(ST_Intersection(way,SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913))),%s,%s,%s,%s),0) as %s, 'coastline' as "natural" from - (select (ST_Dump(ST_Multi(ST_SimplifyPreserveTopology(ST_Buffer(way,-%s),%s)))).geom as %s from - (select ST_Union(way) as %s from - (select ST_Buffer(SetSRID(the_geom,900913), %s) as %s from - %s - where - SetSRID(the_geom,900913) && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) - ) p - ) p - where ST_Area(way) > %s - ) p - """%(bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], - -bbox_p[0],-bbox_p[1],intscalefactor/(bbox_p[2]-bbox_p[0]),intscalefactor/(bbox_p[3]-bbox_p[1]), - geomcolumn, - pixel_size_at_zoom(zoom, pxtolerance),pixel_size_at_zoom(zoom, pxtolerance), - geomcolumn, - geomcolumn, - pixel_size_at_zoom(zoom, pxtolerance), - geomcolumn, - table[vec], - bbox_p[0],bbox_p[1],bbox_p[2],bbox_p[3], - pixel_size_at_zoom(zoom, pxtolerance)**2 - ) - #print query - a = psycopg2.connect(database) - b = a.cursor() - b.execute(query) - names = [q[0] for q in b.description] - - ROWS_FETCHED = 0 - polygons = [] - - for row in b.fetchall(): - ROWS_FETCHED += 1 - geom = dict(map(None,names,row)) - for t in geom.keys(): - if not geom[t]: - del geom[t] - geojson = json.loads(geom[geomcolumn]) - del geom[geomcolumn] - if geojson["type"] == "GeometryCollection": - continue - if "reprpoint" in geom: - geojson["reprpoint"] = json.loads(geom["reprpoint"])["coordinates"] - del geom["reprpoint"] - prop = {} - for k,v in geom.iteritems(): - prop[k] = v - try: - if int(v) == float(v): - prop[k] = int(v) - else: - prop[k] = float(v) - if str(prop[k]) != v: # leading zeros etc.. should be saved - prop[k] = v - except: - pass - geojson["properties"] = prop - polygons.append(geojson) - return {"bbox": bbox, "granularity":intscalefactor, "features":polygons} + for row in b.fetchall(): + ROWS_FETCHED += 1 + geom = dict(map(None,names,row)) + for t in geom.keys(): + if not geom[t]: + del geom[t] + geojson = json.loads(geom[geomcolumn]) + del geom[geomcolumn] + if geojson["type"] == "GeometryCollection": + continue + if "reprpoint" in geom: + geojson["reprpoint"] = json.loads(geom["reprpoint"])["coordinates"] + del geom["reprpoint"] + prop = {} + for k,v in geom.iteritems(): + prop[k] = v + try: + if int(v) == float(v): + prop[k] = int(v) + else: + prop[k] = float(v) + if str(prop[k]) != v: # leading zeros etc.. should be saved + prop[k] = v + except: + pass + geojson["properties"] = prop + polygons.append(geojson) + return {"bbox": bbox, "granularity":intscalefactor, "features":polygons} @@ -203,19 +203,19 @@ print form = cgi.FieldStorage() if "z" not in form: - print "need z" - exit() + print "need z" + exit() if "x" not in form: - print "need x" - exit() + print "need x" + exit() if "y" not in form: - print "need y" - exit() + print "need y" + exit() z = int(form["z"].value) x = int(form["x"].value) y = int(form["y"].value) if z>22: - exit() + exit() callback = "onKothicDataResponse" bbox = projections.bbox_by_tile(z+1,x,y,"EPSG:3857") @@ -235,13 +235,12 @@ dir = "/var/www/vtile/%s/%s/"%(z,x) file = "%s.js"%y try: - if not os.path.exists(dir): - os.makedirs(dir) + if not os.path.exists(dir): + os.makedirs(dir) except: - pass + pass file = open(dir+file,"w") file.write(aaaa) file.flush() file.close() - diff --git a/src/libkomapnik.py b/src/libkomapnik.py index 9f34a05..e77e789 100644 --- a/src/libkomapnik.py +++ b/src/libkomapnik.py @@ -43,349 +43,349 @@ substyles = [] last_id = 0 def get_id(i = 0): - global last_id - last_id += i - return last_id + global last_id + last_id += i + return last_id def zoom_to_scaledenom(z1,z2=False): - """ - Converts zoom level to mapnik's scaledenominator pair for EPSG:3857 - """ - if not z2: - z2 = z1 - s = 279541132.014 - z1 = (s/(2**(z1-1))+s/(2**(z1-2)))/2 - z2 = (s/(2**(z2-1))+s/(2**z2))/2 - #return 100000000000000, 1 - return z1, z2 + """ + Converts zoom level to mapnik's scaledenominator pair for EPSG:3857 + """ + if not z2: + z2 = z1 + s = 279541132.014 + z1 = (s/(2**(z1-1))+s/(2**(z1-2)))/2 + z2 = (s/(2**(z2-1))+s/(2**z2))/2 + #return 100000000000000, 1 + return z1, z2 def pixel_size_at_zoom(z, l=1): - """ - Converts l pixels on tiles into length on zoom z - """ - return int(math.ceil(l* 20037508.342789244 / 256 * 2 / (2**z))) + """ + Converts l pixels on tiles into length on zoom z + """ + return int(math.ceil(l* 20037508.342789244 / 256 * 2 / (2**z))) def xml_fontset(name, unicode=True): - if unicode: - unicode = '' - return """ - - - %s - """%(name, name, unicode) - + if unicode: + unicode = '' + return """ + + + %s + """%(name, name, unicode) + def xml_pointsymbolizer(path="", width="", height="", opacity=1, overlap="false"): - if width: - width =' width="%s" '%width - if height: - height =' height="%s" '%height - return """ - """\ - %(os.path.join(icons_path, path), width, height, opacity, overlap) + if width: + width =' width="%s" '%width + if height: + height =' height="%s" '%height + return """ + """\ + %(os.path.join(icons_path, path), width, height, opacity, overlap) def xml_linesymbolizer(color="#000000", width="1", opacity="1", linecap="butt", linejoin="round", dashes="", smooth=0, zoom=200): - color = nicecolor(color) - linecap = {"none":"butt",}.get(linecap.lower(), linecap) + color = nicecolor(color) + linecap = {"none":"butt",}.get(linecap.lower(), linecap) - if dashes: - dashes = 'stroke-dasharray="%s"'%(dashes) - else: - dashes = "" + if dashes: + dashes = 'stroke-dasharray="%s"'%(dashes) + else: + dashes = "" - if smooth: - smooth = 'smooth="%s"'%(smooth) - else: - smooth = "" + if smooth: + smooth = 'smooth="%s"'%(smooth) + else: + smooth = "" - rasterizer = "" + rasterizer = "" # if float(width) < 4 and not dashes and zoom < 6: # rasterizer = ' rasterizer="fast"' - return """ - """%(rasterizer, smooth, color, float(width), float(opacity), linejoin, linecap, dashes) + return """ + """%(rasterizer, smooth, color, float(width), float(opacity), linejoin, linecap, dashes) def xml_polygonsymbolizer(color="#000000", opacity="1", smooth='0'): - color = nicecolor(color) - if smooth: - smooth = 'smooth="%s"'%(smooth) - else: - smooth = "" - return """ - """%(color, float(opacity), smooth) + color = nicecolor(color) + if smooth: + smooth = 'smooth="%s"'%(smooth) + else: + smooth = "" + return """ + """%(color, float(opacity), smooth) def xml_polygonpatternsymbolizer(file=""): - return """ - """%(os.path.join(icons_path,file)) + return """ + """%(os.path.join(icons_path,file)) def xml_linepatternsymbolizer(file=""): - return """ - """%(os.path.join(icons_path,file)) + return """ + """%(os.path.join(icons_path,file)) def xml_textsymbolizer( text="name",face="DejaVu Sans Book",size="10",color="#000000", halo_color="#ffffff", halo_radius="0", placement="line", offset="0", overlap="false", distance="26", wrap_width=256, align="center", opacity="1", pos="X", transform = "none", spacing="4096"): - color = nicecolor(color) - halo_color = nicecolor(halo_color) - pos = pos.replace("exact", "X").replace("any","S, E, X, N, W, NE, SE, NW, SW").split(",") - pos.extend([str(int(float(x)*text_scale)) for x in size.split(",")]) - pos = ",".join(pos) - size = str(int(float(size.split(",")[0])*text_scale)) - - - placement = {"center": "interior"}.get(placement.lower(), placement) - align = {"center": "middle"}.get(align.lower(), align) - dy = int(float(offset)) - dx = 0 - if align in ("right",'left'): - dx = dy - dy = 0 - - return """ - [%s] - """%(face,int(float(size)),color,halo_color,halo_radius,placement,dx,dy,overlap,wrap_width,distance,align,opacity,pos, transform, spacing,text) + color = nicecolor(color) + halo_color = nicecolor(halo_color) + pos = pos.replace("exact", "X").replace("any","S, E, X, N, W, NE, SE, NW, SW").split(",") + pos.extend([str(int(float(x)*text_scale)) for x in size.split(",")]) + pos = ",".join(pos) + size = str(int(float(size.split(",")[0])*text_scale)) + + + placement = {"center": "interior"}.get(placement.lower(), placement) + align = {"center": "middle"}.get(align.lower(), align) + dy = int(float(offset)) + dx = 0 + if align in ("right",'left'): + dx = dy + dy = 0 + + return """ + [%s] + """%(face,int(float(size)),color,halo_color,halo_radius,placement,dx,dy,overlap,wrap_width,distance,align,opacity,pos, transform, spacing,text) def xml_shieldsymbolizer(path="", width="", height="", text="name",face="DejaVu Sans Book",size="10",color="#000000", halo_color="#ffffff", halo_radius="0", placement="line", offset="0", overlap="false", distance="26", wrap_width=256, align="center", opacity="1", transform="none", unlock_image='true', spacing='500'): - color = nicecolor(color) - halo_color = nicecolor(halo_color) - placement = {"center": "point"}.get(placement.lower(), placement) - align = {"center": "middle"}.get(align.lower(), align) - size = size.split(",")[0] - if width: - width =' width="%s" '%width - if height: - height =' height="%s" '%height - return """ - [%s] - """%(icons_path, \ - path, width, height,face,int(float(size)*text_scale),color,halo_color,halo_radius,placement,offset,overlap,wrap_width,distance,align,opacity, transform, unlock_image, spacing,text ) + color = nicecolor(color) + halo_color = nicecolor(halo_color) + placement = {"center": "point"}.get(placement.lower(), placement) + align = {"center": "middle"}.get(align.lower(), align) + size = size.split(",")[0] + if width: + width =' width="%s" '%width + if height: + height =' height="%s" '%height + return """ + [%s] + """%(icons_path, \ + path, width, height,face,int(float(size)*text_scale),color,halo_color,halo_radius,placement,offset,overlap,wrap_width,distance,align,opacity, transform, unlock_image, spacing,text ) def xml_filter(string): - return """ - %s"""%string + return """ + %s"""%string def xml_scaledenominator(z1, z2=False): - zz1, zz2 = zoom_to_scaledenom(z1,z2) - return """ - %s - %s"""%(zz1,zz2,z1,z2) + zz1, zz2 = zoom_to_scaledenom(z1,z2) + return """ + %s + %s"""%(zz1,zz2,z1,z2) def xml_start(bgcolor="transparent"): - if bgcolor != "transparent": - bgcolor = nicecolor(bgcolor) - return """ - - -"""%(bgcolor, map_proj) + if bgcolor != "transparent": + bgcolor = nicecolor(bgcolor) + return """ + + + """%(bgcolor, map_proj) def xml_end(): - return """ -""" + return """ + """ def xml_style_start(): - global substyles - layer_id = get_id(1) - substyles.append(layer_id) - return """ - """ + return """ + """ def xml_rule_start(): - return """ - """ + return """ + """ def xml_rule_end(): - return """ - """ + return """ + """ def xml_cleantopo(zoom, x_scale, demramp): - return """ - + return """ + - - elevation1z%s - - %s - gdal - 1 - 3857 - - -""" % (zoom, x_scale, demramp, zoom, zoom, cleantopo_dem_path) + + elevation1z%s + + %s + gdal + 1 + 3857 + + + """ % (zoom, x_scale, demramp, zoom, zoom, cleantopo_dem_path) def xml_srtm(zoom, x_scale, demramp): - return """ - + return """ + + + + elevationz%s + + %s + gdal + 1 + 3857 + + + """ % (zoom, x_scale, demramp, zoom, zoom, srtm_dem_path) - - elevationz%s - - %s - gdal - 1 - 3857 - - - """ % (zoom, x_scale, demramp, zoom, zoom, srtm_dem_path) - def xml_hillshade(zoom, x_scale): - hs_path = cleantopo_hs_path - if zoom>6: - hs_path = srtm_hs_path - return """ - + hs_path = cleantopo_hs_path + if zoom>6: + hs_path = srtm_hs_path + return """ + + + + hillshade%s + + %s + gdal + 1 + 3857 + + + """ % (zoom, x_scale, zoom, zoom, hs_path) + - - hillshade%s - - %s - gdal - 1 - 3857 - - - """ % (zoom, x_scale, zoom, zoom, hs_path) - - def xml_hardcoded_arrows(): - return """ - - - - -""" + return """ + + + + + """ def xml_layer(type="postgis", geom="point", interesting_tags = "*", sql = "true", zoom=0 ): - layer_id = get_id(1) - global substyles - subs = "\n".join(["s%s"%i for i in substyles]) - substyles = [] - intersection_SQL = "" - if zoom < 5: - intersection_SQL = '1' - elif zoom > 16: - intersection_SQL = '500000000000' - - - if type == "postgis": - waystring = 'way' - if zoom < 10: - waystring = "ST_Simplify(way, %s) as way"%(pixel_size_at_zoom(zoom,0.6)) - if zoom >= 5: - sql = 'way && !bbox! and '+ sql - if geom == "polygon": - sql = 'way_area > %s and '%(pixel_size_at_zoom(zoom,0.1)**2)+ sql - interesting_tags = list(interesting_tags) - if '"' not in "".join(interesting_tags) and "->" not in "".join(interesting_tags): - interesting_tags = "\", \"".join(interesting_tags) - interesting_tags = "\""+ interesting_tags+"\"" - else: - interesting_tags = ", ".join(interesting_tags) - - return """ - - %s - - - ( -- zoom %s - select %s, %s - from %s%s - where %s - ) as k_layer - - %s - postgis - true - %s - %s - %s - way - %s%s - false - -20037508.342789244, -20037508.342780735, 20037508.342789244, 20037508.342780709 - - """%(layer_id, db_proj, subs, zoom, interesting_tags, waystring, table_prefix, geom, sql, intersection_SQL, db_user, db_name, db_srid, table_prefix, geom) - elif type == "postgis-process": - return """ - - %s - - - ( -- zoom %s - %s - ) as k_layer - - %s - postgis - true - %s - %s - %s - way - %s%s - false - -20037508.342789244, -20037508.342780735, 20037508.342789244, 20037508.342780709 - - """%(layer_id, db_proj, subs, zoom, sql, intersection_SQL, db_user, db_name, db_srid, table_prefix, geom) - elif type == "coast": - if zoom < 9: - return """ - - %s - - shape - %sshoreline_300 - - """%(layer_id, db_proj, subs, world_bnd_path) - else: - return """ - - %s - - shape - %sprocessed_p - - """%(layer_id, db_proj, subs, world_bnd_path) + layer_id = get_id(1) + global substyles + subs = "\n".join(["s%s"%i for i in substyles]) + substyles = [] + intersection_SQL = "" + if zoom < 5: + intersection_SQL = '1' + elif zoom > 16: + intersection_SQL = '500000000000' + + + if type == "postgis": + waystring = 'way' + if zoom < 10: + waystring = "ST_Simplify(way, %s) as way"%(pixel_size_at_zoom(zoom,0.6)) + if zoom >= 5: + sql = 'way && !bbox! and '+ sql + if geom == "polygon": + sql = 'way_area > %s and '%(pixel_size_at_zoom(zoom,0.1)**2)+ sql + interesting_tags = list(interesting_tags) + if '"' not in "".join(interesting_tags) and "->" not in "".join(interesting_tags): + interesting_tags = "\", \"".join(interesting_tags) + interesting_tags = "\""+ interesting_tags+"\"" + else: + interesting_tags = ", ".join(interesting_tags) + + return """ + + %s + + + ( -- zoom %s + select %s, %s + from %s%s + where %s + ) as k_layer + + %s + postgis + true + %s + %s + %s + way + %s%s + false + -20037508.342789244, -20037508.342780735, 20037508.342789244, 20037508.342780709 + + """%(layer_id, db_proj, subs, zoom, interesting_tags, waystring, table_prefix, geom, sql, intersection_SQL, db_user, db_name, db_srid, table_prefix, geom) + elif type == "postgis-process": + return """ + + %s + + + ( -- zoom %s + %s + ) as k_layer + + %s + postgis + true + %s + %s + %s + way + %s%s + false + -20037508.342789244, -20037508.342780735, 20037508.342789244, 20037508.342780709 + + """%(layer_id, db_proj, subs, zoom, sql, intersection_SQL, db_user, db_name, db_srid, table_prefix, geom) + elif type == "coast": + if zoom < 9: + return """ + + %s + + shape + %sshoreline_300 + + """%(layer_id, db_proj, subs, world_bnd_path) + else: + return """ + + %s + + shape + %sprocessed_p + + """%(layer_id, db_proj, subs, world_bnd_path) def xml_nolayer(): - global substyles - substyles = [] + global substyles + substyles = [] def xml_nosubstyle(): - global substyles - substyles = substyles[:-1] \ No newline at end of file + global substyles + substyles = substyles[:-1] diff --git a/src/make_postgis_style.py b/src/make_postgis_style.py index dccd26c..08d1b4b 100644 --- a/src/make_postgis_style.py +++ b/src/make_postgis_style.py @@ -24,8 +24,8 @@ from mapcss import MapCSS langs = ['int_name', 'name:af', 'name:am', 'name:ar', 'name:be', 'name:bg', 'name:br', 'name:ca', 'name:cs', 'name:cy', 'name:de', 'name:el', 'name:en', 'name:eo', 'name:es', 'name:et', 'name:eu', 'name:fa', 'name:fi', 'name:fr', 'name:fur', 'name:fy', 'name:ga', 'name:gd', 'name:gsw', 'name:he', 'name:hi', 'name:hr', 'name:hsb', 'name:hu', 'name:hy', 'name:it', 'name:ja', 'name:ja_kana', 'name:ja_rm', 'name:ka', 'name:kk', 'name:kn', 'name:ko', 'name:ko_rm', 'name:ku', 'name:la', 'name:lb', 'name:lt', 'name:lv', 'name:mk', 'name:mn', 'name:nl', 'name:pl', 'name:pt', 'name:ro', 'name:ru', 'name:sk', 'name:sl', 'name:sq', 'name:sr', 'name:sv', 'name:th', 'name:tr', 'name:uk', 'name:vi', 'name:zh', 'name:zh_pinyin'] if len(sys.argv) < 2: - print "Usage: make_postgis_style.py [stylesheet] [additional_tag,tag2,tag3]" - exit() + print "Usage: make_postgis_style.py [stylesheet] [additional_tag,tag2,tag3]" + exit() style = MapCSS(1, 19) #zoom levels style.parse(open(sys.argv[1],"r").read()) @@ -33,33 +33,33 @@ style.parse(open(sys.argv[1],"r").read()) dct = {} if len(sys.argv) >= 3: - langs.extend(sys.argv[2].split(",")) - dct = dict([(k,set([("node", "linear"), ('way', 'linear')])) for k in langs]) + langs.extend(sys.argv[2].split(",")) + dct = dict([(k,set([("node", "linear"), ('way', 'linear')])) for k in langs]) t = {"node":("node", "linear"), "line":("way", "linear"), "area":("way", "polygon")} for a in t: - for tag in style.get_interesting_tags(type=a): - if tag not in dct: - dct[tag] = set() - dct[tag].add(t[a]) + for tag in style.get_interesting_tags(type=a): + if tag not in dct: + dct[tag] = set() + dct[tag].add(t[a]) print """ # OsmType Tag DataType Flags""" for t in ("z_order","way_area",":area"): - if t in dct: - del dct[t] + if t in dct: + del dct[t] keys = dct.keys() keys.sort() for k in keys: - v = dct[k] - s = ",".join(set([i[0] for i in v])) - pol = "linear" - if "polygon" in set([i[1] for i in v]): - pol = "polygon" - print "%-10s %-20s %-13s %s"%(s, k, "text", pol) + v = dct[k] + s = ",".join(set([i[0] for i in v])) + pol = "linear" + if "polygon" in set([i[1] for i in v]): + pol = "polygon" + print "%-10s %-20s %-13s %s"%(s, k, "text", pol) print """ node,way z_order int4 linear # This is calculated during import way way_area real # This is calculated during import""" diff --git a/src/osm2tiles.py b/src/osm2tiles.py index 8f6c5ab..e7b00b7 100644 --- a/src/osm2tiles.py +++ b/src/osm2tiles.py @@ -23,13 +23,13 @@ from twms import projections from style import Styling reload(sys) -sys.setdefaultencoding("utf-8") # a hack to support UTF-8 +sys.setdefaultencoding("utf-8") # a hack to support UTF-8 try: - import psyco - psyco.full() + import psyco + psyco.full() except ImportError: - pass + pass MAXZOOM = 16 TEMPORARY_FILE_PATH = 'temp_file.bin' @@ -41,179 +41,179 @@ style = Styling() # elsif($k eq 'highway' and $v eq 'footway' or $v eq 'path' or $v eq 'track'){ def tilelist_by_geometry(way, start_zoom = 0, ispoly = False): - """ - Gives a number of (z,x,y) tile numbers that geometry crosses. - """ - ret = set([]) - tiles_by_zooms = {} # zoom: set(tile,tile,tile...) - for t in xrange(0,MAXZOOM+1): - tiles_by_zooms[t] = set([]) - for point in way: - tile = projections.tile_by_coords(point, MAXZOOM, proj) - tile = (MAXZOOM, int(tile[0]),int(tile[1])) - tiles_by_zooms[MAXZOOM].add(tile) - for t in xrange(MAXZOOM-1,start_zoom-1,-1): - for tt in tiles_by_zooms[t+1]: - tiles_by_zooms[t].add((t, int(tt[1]/2), int(tt[2]/2))) - for z in tiles_by_zooms.values(): - ret.update(z) - return ret + """ + Gives a number of (z,x,y) tile numbers that geometry crosses. + """ + ret = set([]) + tiles_by_zooms = {} # zoom: set(tile,tile,tile...) + for t in xrange(0,MAXZOOM+1): + tiles_by_zooms[t] = set([]) + for point in way: + tile = projections.tile_by_coords(point, MAXZOOM, proj) + tile = (MAXZOOM, int(tile[0]),int(tile[1])) + tiles_by_zooms[MAXZOOM].add(tile) + for t in xrange(MAXZOOM-1,start_zoom-1,-1): + for tt in tiles_by_zooms[t+1]: + tiles_by_zooms[t].add((t, int(tt[1]/2), int(tt[2]/2))) + for z in tiles_by_zooms.values(): + ret.update(z) + return ret def pix_distance(a,b,z): - """ - Calculates onscreen distance between 2 points on given zoom. - """ - return 2**z*256*(((a[0]-b[0])/360.)**2+((a[1]-b[1])/180.)**2)**0.5 + """ + Calculates onscreen distance between 2 points on given zoom. + """ + return 2**z*256*(((a[0]-b[0])/360.)**2+((a[1]-b[1])/180.)**2)**0.5 def sanitize(string): - string=string.replace(" ", "_") - string=string.replace(";", ",") - string=string.replace("=", "###") - return string + string=string.replace(" ", "_") + string=string.replace(";", ",") + string=string.replace("=", "###") + return string print sanitize (" ;=") def initDB(filename): - conn = sqlite3.connect(filename) - c = conn.cursor() - # create table - c.execute('''CREATE TABLE nodes (id integer, lat real, lon real)''') - # create index in the id column - # - this makes node id lookup MUCH faster - c.execute('''CREATE INDEX id_idx ON nodes(id)''') - return conn + conn = sqlite3.connect(filename) + c = conn.cursor() + # create table + c.execute('''CREATE TABLE nodes (id integer, lat real, lon real)''') + # create index in the id column + # - this makes node id lookup MUCH faster + c.execute('''CREATE INDEX id_idx ON nodes(id)''') + return conn def storeNode(conn, id, lat, lon): # conn.execute("INSERT INTO nodes VALUES ('%d', %f, %f)" % (id, lat, lon)) - conn.execute("INSERT INTO nodes(id, lat, lon) values (?, ?, ?)", (id, lat, lon)) + conn.execute("INSERT INTO nodes(id, lat, lon) values (?, ?, ?)", (id, lat, lon)) def getNode(conn, id): # conn.execute("SELECT * FROM nodes WHERE id = '%s'" % id) - return conn.execute("SELECT lat, lon FROM nodes WHERE id = '%s'" % id).fetchone() + return conn.execute("SELECT lat, lon FROM nodes WHERE id = '%s'" % id).fetchone() def main (): - DROPPED_POINTS = 0 - WAYS_WRITTEN = 0 - NODES_READ = 0 - WAYS_READ = 0 - tilefiles = {} - tilefiles_hist = [] - #osm_infile = open("minsk.osm", "rb") - osm_infile = sys.stdin + DROPPED_POINTS = 0 + WAYS_WRITTEN = 0 + NODES_READ = 0 + WAYS_READ = 0 + tilefiles = {} + tilefiles_hist = [] + #osm_infile = open("minsk.osm", "rb") + osm_infile = sys.stdin - # remove any stale temporary files - if os.path.exists(TEMPORARY_FILE_PATH): - os.remove(TEMPORARY_FILE_PATH) - conn = initDB(TEMPORARY_FILE_PATH) + # remove any stale temporary files + if os.path.exists(TEMPORARY_FILE_PATH): + os.remove(TEMPORARY_FILE_PATH) + conn = initDB(TEMPORARY_FILE_PATH) # nodes = {} - curway = [] - tags = {} - context = etree.iterparse(osm_infile) - for action, elem in context: - items = dict(elem.items()) - if elem.tag == "node": - NODES_READ += 1 - if NODES_READ % 10000 == 0: - print "Nodes read:", NODES_READ - print len(curway), len(tags), len(tilefiles), len(tilefiles_hist) - if NODES_READ % 100000 == 0: - conn.commit() - print "flushing to temporary storage" + curway = [] + tags = {} + context = etree.iterparse(osm_infile) + for action, elem in context: + items = dict(elem.items()) + if elem.tag == "node": + NODES_READ += 1 + if NODES_READ % 10000 == 0: + print "Nodes read:", NODES_READ + print len(curway), len(tags), len(tilefiles), len(tilefiles_hist) + if NODES_READ % 100000 == 0: + conn.commit() + print "flushing to temporary storage" # nodes[str(items["id"])] = (float(items["lon"]), float(items["lat"])) - storeNode(conn, int(items["id"]), float(items["lon"]), float(items["lat"])) - tags = {} - elif elem.tag == "nd": - result = getNode(conn, int(items["ref"])) - if result: - curway.append(result) + storeNode(conn, int(items["id"]), float(items["lon"]), float(items["lat"])) + tags = {} + elif elem.tag == "nd": + result = getNode(conn, int(items["ref"])) + if result: + curway.append(result) # try: # curway.append(nodes[str(items["ref"])]) # except KeyError: # pass - elif elem.tag == "tag": - tags[sanitize(items["k"])] = sanitize(items["v"]) - elif elem.tag == "way": - WAYS_READ += 1 - if WAYS_READ % 1000 == 0: - print "Ways read:", WAYS_READ - - mzoom = 1 - #tags = style.filter_tags(tags) - if tags: - if True:#style.get_style("way", tags, True): # if way is stylized - towrite = ";".join(["%s=%s"%x for x in tags.iteritems()]) ### TODO: sanitize keys and values - #print towrite - way_simplified = {MAXZOOM: curway} + elif elem.tag == "tag": + tags[sanitize(items["k"])] = sanitize(items["v"]) + elif elem.tag == "way": + WAYS_READ += 1 + if WAYS_READ % 1000 == 0: + print "Ways read:", WAYS_READ - for zoom in xrange(MAXZOOM-1,-1,-1): ######## generalize a bit - # TODO: Douglas-Peucker - prev_point = curway[0] - way = [prev_point] - for point in way_simplified[zoom+1]: - if pix_distance(point, prev_point, zoom) > 1.5: - way.append(point) - prev_point = point - else: - DROPPED_POINTS += 1 + mzoom = 1 + #tags = style.filter_tags(tags) + if tags: + if True:#style.get_style("way", tags, True): # if way is stylized + towrite = ";".join(["%s=%s"%x for x in tags.iteritems()]) ### TODO: sanitize keys and values + #print towrite + way_simplified = {MAXZOOM: curway} - if len(way) == 1: - mzoom = zoom - #print zoom - break - if len(way) > 1: - way_simplified[zoom] = way - #print way - for tile in tilelist_by_geometry(curway, mzoom+1): - z, x, y = tile - path = "tiles/z%s/%s/x%s/%s/"%(z, x/1024, x, y/1024) - if tile not in tilefiles: + for zoom in xrange(MAXZOOM-1,-1,-1): ######## generalize a bit + # TODO: Douglas-Peucker + prev_point = curway[0] + way = [prev_point] + for point in way_simplified[zoom+1]: + if pix_distance(point, prev_point, zoom) > 1.5: + way.append(point) + prev_point = point + else: + DROPPED_POINTS += 1 - if not os.path.exists(path): - os.makedirs(path) - tilefiles[tile] = open(path+"y"+str(y)+".vtile","wb") - tilefiles_hist.append(tile) - else: - if not tilefiles[tile]: - tilefiles[tile] = open(path+"y"+str(y)+".vtile","a") - tilefiles_hist.append(tile) - tilefiles_hist.remove(tile) - tilefiles_hist.append(tile) - print >>tilefiles[tile], "%s %s" % (towrite, items["id"]), " ".join([str(x[0])+" "+str(x[1]) for x in way_simplified[tile[0]]]) - if len(tilefiles_hist) > 400: - print "Cleaned up tiles. Wrote by now:", len(tilefiles),"active:",len(tilefiles_hist) - for tile in tilefiles_hist[0:len(tilefiles_hist)-100]: + if len(way) == 1: + mzoom = zoom + #print zoom + break + if len(way) > 1: + way_simplified[zoom] = way + #print way + for tile in tilelist_by_geometry(curway, mzoom+1): + z, x, y = tile + path = "tiles/z%s/%s/x%s/%s/"%(z, x/1024, x, y/1024) + if tile not in tilefiles: + + if not os.path.exists(path): + os.makedirs(path) + tilefiles[tile] = open(path+"y"+str(y)+".vtile","wb") + tilefiles_hist.append(tile) + else: + if not tilefiles[tile]: + tilefiles[tile] = open(path+"y"+str(y)+".vtile","a") + tilefiles_hist.append(tile) + tilefiles_hist.remove(tile) + tilefiles_hist.append(tile) + print >>tilefiles[tile], "%s %s" % (towrite, items["id"]), " ".join([str(x[0])+" "+str(x[1]) for x in way_simplified[tile[0]]]) + if len(tilefiles_hist) > 400: + print "Cleaned up tiles. Wrote by now:", len(tilefiles),"active:",len(tilefiles_hist) + for tile in tilefiles_hist[0:len(tilefiles_hist)-100]: - tilefiles_hist.remove(tile) - tilefiles[tile].flush() - tilefiles[tile].close() - tilefiles[tile] = None + tilefiles_hist.remove(tile) + tilefiles[tile].flush() + tilefiles[tile].close() + tilefiles[tile] = None - #print >>corr, "%s %s %s %s %s %s"% (curway[0][0],curway[0][1],curway[1][0],curway[1][1], user, ts ) - WAYS_WRITTEN += 1 - if WAYS_WRITTEN % 10000 == 0: - print WAYS_WRITTEN - curway = [] - tags = {} - elem.clear() - # extra insurance - del elem - #user = default_user - #ts = "" - print "Tiles generated:",len(tilefiles) - print "Nodes dropped when generalizing:", DROPPED_POINTS + #print >>corr, "%s %s %s %s %s %s"% (curway[0][0],curway[0][1],curway[1][0],curway[1][1], user, ts ) + WAYS_WRITTEN += 1 + if WAYS_WRITTEN % 10000 == 0: + print WAYS_WRITTEN + curway = [] + tags = {} + elem.clear() + # extra insurance + del elem + #user = default_user + #ts = "" + print "Tiles generated:",len(tilefiles) + print "Nodes dropped when generalizing:", DROPPED_POINTS # print "Nodes in memory:", len(nodes) - c = conn.cursor() - c.execute('SELECT * from nodes') - print "Nodes in memory:", len(c.fetchall()) + c = conn.cursor() + c.execute('SELECT * from nodes') + print "Nodes in memory:", len(c.fetchall()) - # report temporary file size - print "Temporary file size:", os.path.getsize(TEMPORARY_FILE_PATH) + # report temporary file size + print "Temporary file size:", os.path.getsize(TEMPORARY_FILE_PATH) - # remove temporary files - os.remove(TEMPORARY_FILE_PATH) + # remove temporary files + os.remove(TEMPORARY_FILE_PATH) main() diff --git a/src/production.py b/src/production.py index eb890c1..e768aea 100644 --- a/src/production.py +++ b/src/production.py @@ -23,7 +23,7 @@ This is a module to substitute debug.py in porduction mode. debug = lambda st: None class Timer: - def __init__(self, comment): - pass - def stop(self): - pass \ No newline at end of file + def __init__(self, comment): + pass + def stop(self): + pass diff --git a/src/render.py b/src/render.py index ca9ff33..d641fc3 100644 --- a/src/render.py +++ b/src/render.py @@ -25,527 +25,527 @@ import pangocairo import pango def line(cr, c): - cr.move_to(*c[0]) - for k in c: - cr.line_to(*k) - cr.stroke() + cr.move_to(*c[0]) + for k in c: + cr.line_to(*k) + cr.stroke() def poly(cr, c): - cr.move_to(*c[0]) - for k in c: - cr.line_to(*k) - cr.fill() + cr.move_to(*c[0]) + for k in c: + cr.line_to(*k) + cr.fill() def offset_line(line,offset): - a = [] - prevcoord = line[0] - for coord in line: - if coord != prevcoord: - angle = - math.atan2(coord[1]-prevcoord[1],coord[0]-prevcoord[0]) - dx = offset*math.sin(angle) - dy = offset*math.cos(angle) - a.append((prevcoord[0]+dx,prevcoord[1]+dy)) - a.append((coord[0]+dx,coord[1]+dy)) - prevcoord = coord - return a + a = [] + prevcoord = line[0] + for coord in line: + if coord != prevcoord: + angle = - math.atan2(coord[1]-prevcoord[1],coord[0]-prevcoord[0]) + dx = offset*math.sin(angle) + dy = offset*math.cos(angle) + a.append((prevcoord[0]+dx,prevcoord[1]+dy)) + a.append((coord[0]+dx,coord[1]+dy)) + prevcoord = coord + return a class RasterTile: - def __init__(self, width, height, zoomlevel, data_backend, raster_proj="EPSG:3857"): - self.w = width - self.h = height - self.surface = cairo.ImageSurface(cairo.FORMAT_RGB24, self.w, self.h) - self.offset_x = 0 - self.offset_y = 0 - self.bbox = (0.,0.,0.,0.) - self.bbox_p = (0.,0.,0.,0.) - self.zoomlevel = zoomlevel - self.zoom = None - self.data = data_backend - self.proj = raster_proj - def __del__(self): - del self.surface - def screen2lonlat(self, x, y): - lo1, la1, lo2, la2 = self.bbox_p - debug ("%s %s - %s %s"%(x,y,self.w, self.h)) - debug(self.bbox_p) - return projections.to4326( (1.*x/self.w*(lo2-lo1)+lo1, la2+(1.*y/(self.h)*(la1-la2))),self.proj) - # return (x - self.w/2)/(math.cos(self.center_coord[1]*math.pi/180)*self.zoom) + self.center_coord[0], -(y - self.h/2)/self.zoom + self.center_coord[1] - def lonlat2screen(self, (lon, lat), epsg4326=False): - if epsg4326: - lon, lat = projections.from4326((lon,lat),self.proj) - lo1, la1, lo2, la2 = self.bbox_p - return ((lon-lo1)*(self.w-1)/abs(lo2-lo1), ((la2-lat)*(self.h-1)/(la2-la1))) - # return (lon - self.center_coord[0])*self.lcc*self.zoom + self.w/2, -(lat - self.center_coord[1])*self.zoom + self.h/2 - def update_surface_by_center(self, lonlat, zoom, style): - self.zoom = zoom - xy = projections.from4326(lonlat, self.proj) - xy1 = projections.to4326((xy[0]-40075016*0.5**self.zoom/256*self.w, xy[1]-40075016*0.5**self.zoom/256*self.h), self.proj) - xy2 = projections.to4326((xy[0]+40075016*0.5**self.zoom/256*self.w, xy[1]+40075016*0.5**self.zoom/256*self.h), self.proj) - bbox = (xy1[0],xy1[1],xy2[0],xy2[1]) - debug (bbox) - return self.update_surface(bbox, zoom, style) - - - def update_surface(self, bbox, zoom, style, callback = lambda x=None: None): - rendertimer = Timer("Rendering image") - if "image" not in style.cache: - style.cache["image"] = ImageLoader() - - - timer = Timer("Getting data") - self.zoom = zoom - self.bbox = bbox - self.bbox_p = projections.from4326(bbox,self.proj) - - print self.bbox_p - scale = abs(self.w/(self.bbox_p[0] - self.bbox_p[2])/math.cos(math.pi*(self.bbox[1]+self.bbox[3])/2/180)) - zscale = 0.5*scale - cr = cairo.Context(self.surface) - # getting and setting canvas properties - bgs = style.get_style("canvas", {}, self.zoom, scale, zscale) - if not bgs: - bgs = [{}] - bgs = bgs[0] - cr.rectangle(0, 0, self.w, self.h) - # canvas color and opcity - color = bgs.get("fill-color",(0.7, 0.7, 0.7)) - cr.set_source_rgba(color[0], color[1], color[2], bgs.get("fill-opacity", 1)) - cr.fill() - callback() - - # canvas antialiasing - antialias = bgs.get("antialias", "full") - if antialias == "none": - "no antialiasing enabled" - cr.set_antialias(1) - #cr.font_options_set_antialias(1) - elif antialias == "text": - "only text antialiased" - cr.set_antialias(1) - #cr.font_options_set_antialias(2) - else: - "full antialias" - cr.set_antialias(2) - #cr.font_options_set_antialias(2) + def __init__(self, width, height, zoomlevel, data_backend, raster_proj="EPSG:3857"): + self.w = width + self.h = height + self.surface = cairo.ImageSurface(cairo.FORMAT_RGB24, self.w, self.h) + self.offset_x = 0 + self.offset_y = 0 + self.bbox = (0.,0.,0.,0.) + self.bbox_p = (0.,0.,0.,0.) + self.zoomlevel = zoomlevel + self.zoom = None + self.data = data_backend + self.proj = raster_proj + def __del__(self): + del self.surface + def screen2lonlat(self, x, y): + lo1, la1, lo2, la2 = self.bbox_p + debug ("%s %s - %s %s"%(x,y,self.w, self.h)) + debug(self.bbox_p) + return projections.to4326( (1.*x/self.w*(lo2-lo1)+lo1, la2+(1.*y/(self.h)*(la1-la2))),self.proj) + # return (x - self.w/2)/(math.cos(self.center_coord[1]*math.pi/180)*self.zoom) + self.center_coord[0], -(y - self.h/2)/self.zoom + self.center_coord[1] + def lonlat2screen(self, (lon, lat), epsg4326=False): + if epsg4326: + lon, lat = projections.from4326((lon,lat),self.proj) + lo1, la1, lo2, la2 = self.bbox_p + return ((lon-lo1)*(self.w-1)/abs(lo2-lo1), ((la2-lat)*(self.h-1)/(la2-la1))) + # return (lon - self.center_coord[0])*self.lcc*self.zoom + self.w/2, -(lat - self.center_coord[1])*self.zoom + self.h/2 + def update_surface_by_center(self, lonlat, zoom, style): + self.zoom = zoom + xy = projections.from4326(lonlat, self.proj) + xy1 = projections.to4326((xy[0]-40075016*0.5**self.zoom/256*self.w, xy[1]-40075016*0.5**self.zoom/256*self.h), self.proj) + xy2 = projections.to4326((xy[0]+40075016*0.5**self.zoom/256*self.w, xy[1]+40075016*0.5**self.zoom/256*self.h), self.proj) + bbox = (xy1[0],xy1[1],xy2[0],xy2[1]) + debug (bbox) + return self.update_surface(bbox, zoom, style) - - datatimer = Timer("Asking backend") - if "get_sql_hints" in dir(style): - hints = style.get_sql_hints('way', self.zoom) - else: - hints = None - if "get_interesting_tags" in dir(style): - itags = style.get_interesting_tags(zoom=self.zoom) - else: - itags = None - - # enlarge bbox by 20% to each side. results in more vectors, but makes less artifaces. - span_x, span_y = bbox[2]-bbox[0], bbox[3]-bbox[1] - bbox_expand = [bbox[0]-0.2*span_x,bbox[1]-0.2*span_y,bbox[2]+0.2*span_x,bbox[3]+0.2*span_y] - vectors = self.data.get_vectors(bbox_expand,self.zoom,hints,itags).values() - datatimer.stop() - datatimer = Timer("Applying styles") - ww = [] - - for way in vectors: - type = "line" - if way.coords[0] == way.coords[-1]: - type == "area" - st = style.get_style("area", way.tags, self.zoom, scale, zscale) - if st: - for fpt in st: - #debug(fpt) - ww.append([way.copy(), fpt]) - - datatimer.stop() - debug( "%s objects on screen (%s in dataset)"%(len(ww),len(vectors)) ) - - er = Timer("Projecing data") - if self.data.proj != self.proj: - for w in ww: - w[0].cs = [self.lonlat2screen(coord) for coord in projections.transform(w[0].coords, self.data.proj, self.proj)] - else: - for w in ww: - w[0].cs = [self.lonlat2screen(coord) for coord in w[0].coords] - for w in ww: - if "offset" in w[1]: - offset = float(w[1]["offset"]) - w[0] = w[0].copy() - w[0].cs = offset_line(w[0].cs, offset) - if "raise" in w[1] and not "extrude" in w[1]: - w[0] = w[0].copy() - offset = float(w[1]["raise"]) - w[0].cs_real = w[0].cs - w[0].cs = [(x,y-offset) for x,y in w[0].cs] - if "extrude" in w[1]: - if w[1]["extrude"]<2: - del w[1]["extrude"] - if "extrude" in w[1] and "fill-color" not in w[1] and "width" in w[1]: - w[1]["fill-color"] = w[1].get("color", (0,0,0)) - w[1]["fill-opacity"] = w[1].get("opacity", 1) - w[0] = w[0].copy() - #print w[0].cs - w[0].cs = offset_line(w[0].cs, w[1]["width"]/2) - #print w[0].cs - aa = offset_line(w[0].cs, -w[1]["width"]) - del w[1]["width"] - aa.reverse() - w[0].cs.extend(aa) + def update_surface(self, bbox, zoom, style, callback = lambda x=None: None): + rendertimer = Timer("Rendering image") + if "image" not in style.cache: + style.cache["image"] = ImageLoader() - er.stop() + timer = Timer("Getting data") + self.zoom = zoom + self.bbox = bbox + self.bbox_p = projections.from4326(bbox,self.proj) + print self.bbox_p + scale = abs(self.w/(self.bbox_p[0] - self.bbox_p[2])/math.cos(math.pi*(self.bbox[1]+self.bbox[3])/2/180)) + zscale = 0.5*scale + cr = cairo.Context(self.surface) + # getting and setting canvas properties + bgs = style.get_style("canvas", {}, self.zoom, scale, zscale) + if not bgs: + bgs = [{}] + bgs = bgs[0] + cr.rectangle(0, 0, self.w, self.h) + # canvas color and opcity + color = bgs.get("fill-color",(0.7, 0.7, 0.7)) + cr.set_source_rgba(color[0], color[1], color[2], bgs.get("fill-opacity", 1)) + cr.fill() + callback() - ww.sort(key=lambda x: x[1]["layer"]) - layers = list(set([int(x[1]["layer"]/100.) for x in ww])) - layers.sort() - objs_by_layers = {} - for layer in layers: - objs_by_layers[layer] = [] - for obj in ww: - objs_by_layers[int(obj[1]["layer"]/100.)].append(obj) - - del ww - timer.stop() - timer = Timer("Rasterizing image") - linecaps = {"butt":0, "round":1, "square":2} - linejoin = {"miter":0, "round":1, "bevel":2} - - text_rendered_at = set([(-100,-100)]) - for layer in layers: - data = objs_by_layers[layer] - #data.sort(lambda x,y:cmp(max([x1[1] for x1 in x[0].cs]), max([x1[1] for x1 in y[0].cs]))) - + # canvas antialiasing + antialias = bgs.get("antialias", "full") + if antialias == "none": + "no antialiasing enabled" + cr.set_antialias(1) + #cr.font_options_set_antialias(1) + elif antialias == "text": + "only text antialiased" + cr.set_antialias(1) + #cr.font_options_set_antialias(2) + else: + "full antialias" + cr.set_antialias(2) + #cr.font_options_set_antialias(2) - # - fill polygons - for obj in data: - if ("fill-color" in obj[1] or "fill-image" in obj[1]) and not "extrude" in obj[1]: ## TODO: fill-image - color = obj[1].get("fill-color", (0,0,0)) - cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("fill-opacity", 1)) + datatimer = Timer("Asking backend") + if "get_sql_hints" in dir(style): + hints = style.get_sql_hints('way', self.zoom) + else: + hints = None + if "get_interesting_tags" in dir(style): + itags = style.get_interesting_tags(zoom=self.zoom) + else: + itags = None - if "fill-image" in obj[1]: - image = style.cache["image"][obj[1]["fill-image"]] - if image: - pattern = cairo.SurfacePattern(image) - pattern.set_extend(cairo.EXTEND_REPEAT) - cr.set_source(pattern) - poly(cr, obj[0].cs) + # enlarge bbox by 20% to each side. results in more vectors, but makes less artifaces. + span_x, span_y = bbox[2]-bbox[0], bbox[3]-bbox[1] + bbox_expand = [bbox[0]-0.2*span_x,bbox[1]-0.2*span_y,bbox[2]+0.2*span_x,bbox[3]+0.2*span_y] + vectors = self.data.get_vectors(bbox_expand,self.zoom,hints,itags).values() + datatimer.stop() + datatimer = Timer("Applying styles") + ww = [] - # - draw casings on layer - for obj in data: - ### Extras: casing-linecap, casing-linejoin - if "casing-width" in obj[1] or "casing-color" in obj[1] and "extrude" not in obj[1]: - cr.set_dash(obj[1].get("casing-dashes",obj[1].get("dashes", []))) - cr.set_line_join(linejoin.get(obj[1].get("casing-linejoin",obj[1].get("linejoin", "round")),1)) - color = obj[1].get("casing-color", (0,0,0)) - cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("casing-opacity", 1)) - ## TODO: good combining of transparent lines and casing - ## Probable solution: render casing, render way as mask and put casing with mask chopped out onto image + for way in vectors: + type = "line" + if way.coords[0] == way.coords[-1]: + type == "area" + st = style.get_style("area", way.tags, self.zoom, scale, zscale) + if st: + for fpt in st: + #debug(fpt) + ww.append([way.copy(), fpt]) + + datatimer.stop() + debug( "%s objects on screen (%s in dataset)"%(len(ww),len(vectors)) ) + + er = Timer("Projecing data") + if self.data.proj != self.proj: + for w in ww: + w[0].cs = [self.lonlat2screen(coord) for coord in projections.transform(w[0].coords, self.data.proj, self.proj)] + else: + for w in ww: + w[0].cs = [self.lonlat2screen(coord) for coord in w[0].coords] + for w in ww: + if "offset" in w[1]: + offset = float(w[1]["offset"]) + w[0] = w[0].copy() + w[0].cs = offset_line(w[0].cs, offset) + if "raise" in w[1] and not "extrude" in w[1]: + w[0] = w[0].copy() + offset = float(w[1]["raise"]) + w[0].cs_real = w[0].cs + w[0].cs = [(x,y-offset) for x,y in w[0].cs] + if "extrude" in w[1]: + if w[1]["extrude"]<2: + del w[1]["extrude"] + if "extrude" in w[1] and "fill-color" not in w[1] and "width" in w[1]: + w[1]["fill-color"] = w[1].get("color", (0,0,0)) + w[1]["fill-opacity"] = w[1].get("opacity", 1) + w[0] = w[0].copy() + #print w[0].cs + w[0].cs = offset_line(w[0].cs, w[1]["width"]/2) + #print w[0].cs + aa = offset_line(w[0].cs, -w[1]["width"]) + del w[1]["width"] + aa.reverse() + w[0].cs.extend(aa) - cr.set_line_width (obj[1].get("width",0)+obj[1].get("casing-width", 1 )) - cr.set_line_cap(linecaps.get(obj[1].get("casing-linecap", obj[1].get("linecap", "butt")),0)) - line(cr, obj[0].cs) - - # - draw line centers - for obj in data: - if ("width" in obj[1] or "color" in obj[1] or "image" in obj[1]) and "extrude" not in obj[1]: - cr.set_dash(obj[1].get("dashes", [])) - cr.set_line_join(linejoin.get(obj[1].get("linejoin", "round"),1)) - color = obj[1].get("color", (0,0,0)) - cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("opacity", 1)) - ## TODO: better overlapping of transparent lines. - ## Probable solution: render them (while they're of the same opacity and layer) on a temporary canvas that's merged into main later - cr.set_line_width (obj[1].get("width", 1)) - cr.set_line_cap(linecaps.get(obj[1].get("linecap", "butt"),0)) - if "image" in obj[1]: - image = style.cache["image"][obj[1]["image"]] - if image: - pattern = cairo.SurfacePattern(image) - pattern.set_extend(cairo.EXTEND_REPEAT) - cr.set_source(pattern) - line(cr, obj[0].cs) + er.stop() - callback() + ww.sort(key=lambda x: x[1]["layer"]) + layers = list(set([int(x[1]["layer"]/100.) for x in ww])) + layers.sort() + objs_by_layers = {} + for layer in layers: + objs_by_layers[layer] = [] + for obj in ww: + objs_by_layers[int(obj[1]["layer"]/100.)].append(obj) - # - extruding polygons - #data.sort(lambda x,y:cmp(max([x1[1] for x1 in x[0].cs]), max([x1[1] for x1 in y[0].cs]))) - # Pass 1. Creating list of extruded polygons - extlist = [] - # fromat: (coords, ("h"/"v", y,z), real_obj) - for obj in data: - if "extrude" in obj[1]: - def face_to_poly(face, hgt): - """ - Converts a line into height-up extruded poly - """ - return [face[0], face[1], (face[1][0], face[1][1]-hgt), (face[0][0], face[0][1]-hgt), face[0]] - hgt = obj[1]["extrude"] - raised = float(obj[1].get("raise",0)) - excoords = [(a[0],a[1]-hgt-raised) for a in obj[0].cs] + del ww + timer.stop() + timer = Timer("Rasterizing image") + linecaps = {"butt":0, "round":1, "square":2} + linejoin = {"miter":0, "round":1, "bevel":2} - faces = [] - coord = obj[0].cs[-1] - #p_coord = (coord[0],coord[1]-raised) - p_coord = False - for coord in obj[0].cs: - c = (coord[0],coord[1]-raised) - if p_coord: - extlist.append( (face_to_poly([c, p_coord],hgt), ("v", min(coord[1],p_coord[1]), hgt), obj )) - p_coord = c - - extlist.append( (excoords, ("h", min(coord[1],p_coord[1]), hgt), obj )) - #faces.sort(lambda x,y:cmp(max([x1[1] for x1 in x]), max([x1[1] for x1 in y]))) + text_rendered_at = set([(-100,-100)]) + for layer in layers: + data = objs_by_layers[layer] + #data.sort(lambda x,y:cmp(max([x1[1] for x1 in x[0].cs]), max([x1[1] for x1 in y[0].cs]))) - # Pass 2. Sorting - def compare_things(a,b): - """ - Custom comparator for extlist sorting. - Sorts back-to-front, bottom-to-top, | > \ > _, horizontal-to-vertical. - """ - t1,t2 = a[1],b[1] # - if t1[1] > t2[1]: # back-to-front - return 1 - if t1[1] < t2[1]: - return -1 - if t1[2] > t2[2]: # bottom-to-top - return 1 - if t1[2] < t2[2]: - return -1 - if t1[0] < t2[0]: # h-to-v - return 1 - if t1[0] > t2[0]: - return -1 - - return cmp(math.sin(math.atan2(a[0][0][0]-a[0][1][0],a[0][0][0]-a[0][1][0])),math.sin(math.atan2(b[0][0][0]-b[0][1][0],b[0][0][0]-b[0][1][0]))) - print t1 - print t2 - - extlist.sort(compare_things) - - # Pass 3. Rendering using painter's algorythm - cr.set_dash([]) - for ply, prop, obj in extlist: - if prop[0] == "v": - color = obj[1].get("extrude-face-color", obj[1].get("color", (0,0,0) )) - cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-face-opacity", obj[1].get("opacity", 1))) - poly(cr, ply) - color = obj[1].get("extrude-edge-color", obj[1].get("color", (0,0,0) )) - cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-edge-opacity", obj[1].get("opacity", 1))) - cr.set_line_width (.5) - line(cr, ply) - if prop[0] == "h": - if "fill-color" in obj[1]: - color = obj[1]["fill-color"] - cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("fill-opacity", obj[1].get("opacity", 1))) - poly(cr,ply) - color = obj[1].get("extrude-edge-color", obj[1].get("color", (0,0,0) )) - cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-edge-opacity", obj[1].get("opacity", 1))) - cr.set_line_width (1) - line(cr, ply) - - #cr.set_line_width (obj[1].get("width", 1)) - #color = obj[1].get("color", (0,0,0) ) - #cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-edge-opacity", obj[1].get("opacity", 1))) - #line(cr,excoords) - #if "fill-color" in obj[1]: - #color = obj[1]["fill-color"] - #cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("fill-opacity", 1)) - #poly(cr,excoords) - for obj in data: - if "icon-image" in obj[1]: - image = style.cache["image"][obj[1]["icon-image"]] - if image: - dy = image.get_height()/2 - dx = image.get_width()/2 - - where = self.lonlat2screen(projections.transform(obj[0].center,self.data.proj,self.proj)) - cr.set_source_surface(image, where[0]-dx, where[1]-dy) - cr.paint() - callback() - # - render text labels - texttimer = Timer("Text rendering") - cr.set_line_join(1) # setting linejoin to "round" to get less artifacts on halo render - for obj in data: - if "text" in obj[1]: - - text = obj[1]["text"] - #cr.set_line_width (obj[1].get("width", 1)) - #cr.set_font_size(float(obj[1].get("font-size", 9))) - ft_desc = pango.FontDescription() - - ft_desc.set_family(obj[1].get('font-family', 'sans')) - ft_desc.set_size(pango.SCALE*int(obj[1].get('font-size',9))) - fontstyle = obj[1].get('font-style', 'normal') - if fontstyle == 'italic': - fontstyle = pango.STYLE_ITALIC - else: - fontstyle = pango.STYLE_NORMAL - ft_desc.set_style(fontstyle) - fontweight = obj[1].get('font-weight', 400) - try: - fontweight = int(fontweight) - except ValueError: - if fontweight == 'bold': - fontweight = 700 - else: - fontweight = 400 - ft_desc.set_weight(fontweight) - if obj[1].get('text-transform', None) == 'uppercase': - text = text.upper() - p_ctx = pangocairo.CairoContext(cr) - p_layout = p_ctx.create_layout() - p_layout.set_font_description(ft_desc) - p_layout.set_text(text) - p_attrs = pango.AttrList() - decoration = obj[1].get('text-decoration', 'none') - if decoration == 'underline': - p_attrs.insert(pango.AttrUnderline(pango.UNDERLINE_SINGLE,end_index=-1)) - decoration = obj[1].get('font-variant', 'none') - if decoration == 'small-caps': - p_attrs.insert(pango.AttrVariant(pango.VARIANT_SMALL_CAPS, start_index=0, end_index=-1)) + # - fill polygons + for obj in data: + if ("fill-color" in obj[1] or "fill-image" in obj[1]) and not "extrude" in obj[1]: ## TODO: fill-image + color = obj[1].get("fill-color", (0,0,0)) + cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("fill-opacity", 1)) - p_layout.set_attributes(p_attrs) - - - if obj[1].get("text-position", "center") == "center": - where = self.lonlat2screen(projections.transform(obj[0].center,self.data.proj,self.proj)) - for t in text_rendered_at: - if ((t[0]-where[0])**2+(t[1]-where[1])**2) < 15*15: - break - else: - text_rendered_at.add(where) - #debug ("drawing text: %s at %s"%(text, where)) - if "text-halo-color" in obj[1] or "text-halo-radius" in obj[1]: - cr.new_path() - cr.move_to(where[0], where[1]) - cr.set_line_width (obj[1].get("text-halo-radius", 1)) - color = obj[1].get("text-halo-color", (1.,1.,1.)) - cr.set_source_rgb(color[0], color[1], color[2]) - cr.text_path(text) - cr.stroke() - cr.new_path() - cr.move_to(where[0], where[1]) - cr.set_line_width (obj[1].get("text-halo-radius", 1)) - color = obj[1].get("text-color", (0.,0.,0.)) - cr.set_source_rgb(color[0], color[1], color[2]) - cr.text_path(text) - cr.fill() - else: ### render text along line - c = obj[0].cs - text = unicode(text,"utf-8") - # - calculate line length - length = reduce(lambda x,y: (x[0]+((y[0]-x[1])**2 + (y[1]-x[2])**2 )**0.5, y[0], y[1]), c, (0,c[0][0],c[0][1]))[0] - #print length, text, cr.text_extents(text) - if length > cr.text_extents(text)[2]: + if "fill-image" in obj[1]: + image = style.cache["image"][obj[1]["fill-image"]] + if image: + pattern = cairo.SurfacePattern(image) + pattern.set_extend(cairo.EXTEND_REPEAT) + cr.set_source(pattern) + poly(cr, obj[0].cs) - # - function to get (x, y, normale) from (c, length_along_c) - def get_xy_from_len(c,length_along_c): - x0, y0 = c[0] - - for x,y in c: - seg_len = ((x-x0)**2+(y-y0)**2)**0.5 - if length_along_c < seg_len: - normed = length_along_c /seg_len - return (x-x0)*normed+x0, (y-y0)*normed+y0, math.atan2(y-y0,x-x0) - else: - length_along_c -= seg_len - x0,y0 = x,y - else: - return None - da = 0 - os = 1 - z = length/2-cr.text_extents(text)[2]/2 - # print get_xy_from_len(c,z) - if c[0][0] < c[1][0] and get_xy_from_len(c,z)[2] -math.pi/2: - da = 0 - os = 1 - z = length/2-cr.text_extents(text)[2]/2 - else: - da = math.pi - os = -1 - z = length/2+cr.text_extents(text)[2]/2 - z1=z - if "text-halo-color" in obj[1] or "text-halo-radius" in obj[1]: - cr.set_line_width (obj[1].get("text-halo-radius", 1.5)*2) - color = obj[1].get("text-halo-color", (1.,1.,1.)) - cr.set_source_rgb(color[0], color[1], color[2]) - xy = get_xy_from_len(c,z) - cr.save() - #cr.move_to(xy[0],xy[1]) - p_ctx.translate(xy[0],xy[1]) - cr.rotate(xy[2]+da) - #p_ctx.translate(x,y) - #p_ctx.show_layout(p_layout) - p_ctx.layout_path(p_layout) - - cr.restore() - cr.stroke() - #for letter in text: - #cr.new_path() - #xy = get_xy_from_len(c,z) - ##print letter, cr.text_extents(letter) - #cr.move_to(xy[0],xy[1]) - #cr.save() - #cr.rotate(xy[2]+da) - #cr.text_path(letter) - #cr.restore() - #cr.stroke() - #z += os*cr.text_extents(letter)[4] + # - draw casings on layer + for obj in data: + ### Extras: casing-linecap, casing-linejoin + if "casing-width" in obj[1] or "casing-color" in obj[1] and "extrude" not in obj[1]: + cr.set_dash(obj[1].get("casing-dashes",obj[1].get("dashes", []))) + cr.set_line_join(linejoin.get(obj[1].get("casing-linejoin",obj[1].get("linejoin", "round")),1)) + color = obj[1].get("casing-color", (0,0,0)) + cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("casing-opacity", 1)) + ## TODO: good combining of transparent lines and casing + ## Probable solution: render casing, render way as mask and put casing with mask chopped out onto image - color = obj[1].get("text-color", (0.,0.,0.)) - cr.set_source_rgb(color[0], color[1], color[2]) - z = z1 - xy = get_xy_from_len(c,z) - cr.save() - #cr.move_to(xy[0],xy[1]) - p_ctx.translate(xy[0],xy[1]) - cr.rotate(xy[2]+da) - #p_ctx.translate(x,y) - p_ctx.show_layout(p_layout) - cr.restore() - - #for letter in text: - #cr.new_path() - #xy = get_xy_from_len(c,z) - ##print letter, cr.text_extents(letter) - #cr.move_to(xy[0],xy[1]) - #cr.save() - #cr.rotate(xy[2]+da) - #cr.text_path(letter) - #cr.restore() - #cr.fill() - #z += os*cr.text_extents(letter)[4] - texttimer.stop() - del data - del layers + cr.set_line_width (obj[1].get("width",0)+obj[1].get("casing-width", 1 )) + cr.set_line_cap(linecaps.get(obj[1].get("casing-linecap", obj[1].get("linecap", "butt")),0)) + line(cr, obj[0].cs) + + # - draw line centers + for obj in data: + if ("width" in obj[1] or "color" in obj[1] or "image" in obj[1]) and "extrude" not in obj[1]: + cr.set_dash(obj[1].get("dashes", [])) + cr.set_line_join(linejoin.get(obj[1].get("linejoin", "round"),1)) + color = obj[1].get("color", (0,0,0)) + cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("opacity", 1)) + ## TODO: better overlapping of transparent lines. + ## Probable solution: render them (while they're of the same opacity and layer) on a temporary canvas that's merged into main later + cr.set_line_width (obj[1].get("width", 1)) + cr.set_line_cap(linecaps.get(obj[1].get("linecap", "butt"),0)) + if "image" in obj[1]: + image = style.cache["image"][obj[1]["image"]] + if image: + pattern = cairo.SurfacePattern(image) + pattern.set_extend(cairo.EXTEND_REPEAT) + cr.set_source(pattern) + line(cr, obj[0].cs) + + + callback() + + # - extruding polygons + #data.sort(lambda x,y:cmp(max([x1[1] for x1 in x[0].cs]), max([x1[1] for x1 in y[0].cs]))) + # Pass 1. Creating list of extruded polygons + extlist = [] + # fromat: (coords, ("h"/"v", y,z), real_obj) + for obj in data: + if "extrude" in obj[1]: + def face_to_poly(face, hgt): + """ + Converts a line into height-up extruded poly + """ + return [face[0], face[1], (face[1][0], face[1][1]-hgt), (face[0][0], face[0][1]-hgt), face[0]] + hgt = obj[1]["extrude"] + raised = float(obj[1].get("raise",0)) + excoords = [(a[0],a[1]-hgt-raised) for a in obj[0].cs] + + faces = [] + coord = obj[0].cs[-1] + #p_coord = (coord[0],coord[1]-raised) + p_coord = False + for coord in obj[0].cs: + c = (coord[0],coord[1]-raised) + if p_coord: + extlist.append( (face_to_poly([c, p_coord],hgt), ("v", min(coord[1],p_coord[1]), hgt), obj )) + p_coord = c + + extlist.append( (excoords, ("h", min(coord[1],p_coord[1]), hgt), obj )) + #faces.sort(lambda x,y:cmp(max([x1[1] for x1 in x]), max([x1[1] for x1 in y]))) + + + # Pass 2. Sorting + def compare_things(a,b): + """ + Custom comparator for extlist sorting. + Sorts back-to-front, bottom-to-top, | > \ > _, horizontal-to-vertical. + """ + t1,t2 = a[1],b[1] # + if t1[1] > t2[1]: # back-to-front + return 1 + if t1[1] < t2[1]: + return -1 + if t1[2] > t2[2]: # bottom-to-top + return 1 + if t1[2] < t2[2]: + return -1 + if t1[0] < t2[0]: # h-to-v + return 1 + if t1[0] > t2[0]: + return -1 + + return cmp(math.sin(math.atan2(a[0][0][0]-a[0][1][0],a[0][0][0]-a[0][1][0])),math.sin(math.atan2(b[0][0][0]-b[0][1][0],b[0][0][0]-b[0][1][0]))) + print t1 + print t2 + + extlist.sort(compare_things) + + # Pass 3. Rendering using painter's algorythm + cr.set_dash([]) + for ply, prop, obj in extlist: + if prop[0] == "v": + color = obj[1].get("extrude-face-color", obj[1].get("color", (0,0,0) )) + cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-face-opacity", obj[1].get("opacity", 1))) + poly(cr, ply) + color = obj[1].get("extrude-edge-color", obj[1].get("color", (0,0,0) )) + cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-edge-opacity", obj[1].get("opacity", 1))) + cr.set_line_width (.5) + line(cr, ply) + if prop[0] == "h": + if "fill-color" in obj[1]: + color = obj[1]["fill-color"] + cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("fill-opacity", obj[1].get("opacity", 1))) + poly(cr,ply) + color = obj[1].get("extrude-edge-color", obj[1].get("color", (0,0,0) )) + cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-edge-opacity", obj[1].get("opacity", 1))) + cr.set_line_width (1) + line(cr, ply) + + #cr.set_line_width (obj[1].get("width", 1)) + #color = obj[1].get("color", (0,0,0) ) + #cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-edge-opacity", obj[1].get("opacity", 1))) + #line(cr,excoords) + #if "fill-color" in obj[1]: + #color = obj[1]["fill-color"] + #cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("fill-opacity", 1)) + #poly(cr,excoords) + for obj in data: + if "icon-image" in obj[1]: + image = style.cache["image"][obj[1]["icon-image"]] + if image: + dy = image.get_height()/2 + dx = image.get_width()/2 + + where = self.lonlat2screen(projections.transform(obj[0].center,self.data.proj,self.proj)) + cr.set_source_surface(image, where[0]-dx, where[1]-dy) + cr.paint() + + + callback() + # - render text labels + texttimer = Timer("Text rendering") + cr.set_line_join(1) # setting linejoin to "round" to get less artifacts on halo render + for obj in data: + if "text" in obj[1]: + + text = obj[1]["text"] + #cr.set_line_width (obj[1].get("width", 1)) + #cr.set_font_size(float(obj[1].get("font-size", 9))) + ft_desc = pango.FontDescription() + + ft_desc.set_family(obj[1].get('font-family', 'sans')) + ft_desc.set_size(pango.SCALE*int(obj[1].get('font-size',9))) + fontstyle = obj[1].get('font-style', 'normal') + if fontstyle == 'italic': + fontstyle = pango.STYLE_ITALIC + else: + fontstyle = pango.STYLE_NORMAL + ft_desc.set_style(fontstyle) + fontweight = obj[1].get('font-weight', 400) + try: + fontweight = int(fontweight) + except ValueError: + if fontweight == 'bold': + fontweight = 700 + else: + fontweight = 400 + ft_desc.set_weight(fontweight) + if obj[1].get('text-transform', None) == 'uppercase': + text = text.upper() + p_ctx = pangocairo.CairoContext(cr) + p_layout = p_ctx.create_layout() + p_layout.set_font_description(ft_desc) + p_layout.set_text(text) + p_attrs = pango.AttrList() + decoration = obj[1].get('text-decoration', 'none') + if decoration == 'underline': + p_attrs.insert(pango.AttrUnderline(pango.UNDERLINE_SINGLE,end_index=-1)) + decoration = obj[1].get('font-variant', 'none') + if decoration == 'small-caps': + p_attrs.insert(pango.AttrVariant(pango.VARIANT_SMALL_CAPS, start_index=0, end_index=-1)) + + p_layout.set_attributes(p_attrs) + + + if obj[1].get("text-position", "center") == "center": + where = self.lonlat2screen(projections.transform(obj[0].center,self.data.proj,self.proj)) + for t in text_rendered_at: + if ((t[0]-where[0])**2+(t[1]-where[1])**2) < 15*15: + break + else: + text_rendered_at.add(where) + #debug ("drawing text: %s at %s"%(text, where)) + if "text-halo-color" in obj[1] or "text-halo-radius" in obj[1]: + cr.new_path() + cr.move_to(where[0], where[1]) + cr.set_line_width (obj[1].get("text-halo-radius", 1)) + color = obj[1].get("text-halo-color", (1.,1.,1.)) + cr.set_source_rgb(color[0], color[1], color[2]) + cr.text_path(text) + cr.stroke() + cr.new_path() + cr.move_to(where[0], where[1]) + cr.set_line_width (obj[1].get("text-halo-radius", 1)) + color = obj[1].get("text-color", (0.,0.,0.)) + cr.set_source_rgb(color[0], color[1], color[2]) + cr.text_path(text) + cr.fill() + else: ### render text along line + c = obj[0].cs + text = unicode(text,"utf-8") + # - calculate line length + length = reduce(lambda x,y: (x[0]+((y[0]-x[1])**2 + (y[1]-x[2])**2 )**0.5, y[0], y[1]), c, (0,c[0][0],c[0][1]))[0] + #print length, text, cr.text_extents(text) + if length > cr.text_extents(text)[2]: + + # - function to get (x, y, normale) from (c, length_along_c) + def get_xy_from_len(c,length_along_c): + x0, y0 = c[0] + + for x,y in c: + seg_len = ((x-x0)**2+(y-y0)**2)**0.5 + if length_along_c < seg_len: + normed = length_along_c /seg_len + return (x-x0)*normed+x0, (y-y0)*normed+y0, math.atan2(y-y0,x-x0) + else: + length_along_c -= seg_len + x0,y0 = x,y + else: + return None + da = 0 + os = 1 + z = length/2-cr.text_extents(text)[2]/2 + # print get_xy_from_len(c,z) + if c[0][0] < c[1][0] and get_xy_from_len(c,z)[2] -math.pi/2: + da = 0 + os = 1 + z = length/2-cr.text_extents(text)[2]/2 + else: + da = math.pi + os = -1 + z = length/2+cr.text_extents(text)[2]/2 + z1=z + if "text-halo-color" in obj[1] or "text-halo-radius" in obj[1]: + cr.set_line_width (obj[1].get("text-halo-radius", 1.5)*2) + color = obj[1].get("text-halo-color", (1.,1.,1.)) + cr.set_source_rgb(color[0], color[1], color[2]) + xy = get_xy_from_len(c,z) + cr.save() + #cr.move_to(xy[0],xy[1]) + p_ctx.translate(xy[0],xy[1]) + cr.rotate(xy[2]+da) + #p_ctx.translate(x,y) + #p_ctx.show_layout(p_layout) + p_ctx.layout_path(p_layout) + + cr.restore() + cr.stroke() + #for letter in text: + #cr.new_path() + #xy = get_xy_from_len(c,z) + ##print letter, cr.text_extents(letter) + #cr.move_to(xy[0],xy[1]) + #cr.save() + #cr.rotate(xy[2]+da) + #cr.text_path(letter) + #cr.restore() + #cr.stroke() + #z += os*cr.text_extents(letter)[4] + + color = obj[1].get("text-color", (0.,0.,0.)) + cr.set_source_rgb(color[0], color[1], color[2]) + z = z1 + xy = get_xy_from_len(c,z) + cr.save() + #cr.move_to(xy[0],xy[1]) + p_ctx.translate(xy[0],xy[1]) + cr.rotate(xy[2]+da) + #p_ctx.translate(x,y) + p_ctx.show_layout(p_layout) + cr.restore() + + #for letter in text: + #cr.new_path() + #xy = get_xy_from_len(c,z) + ##print letter, cr.text_extents(letter) + #cr.move_to(xy[0],xy[1]) + #cr.save() + #cr.rotate(xy[2]+da) + #cr.text_path(letter) + #cr.restore() + #cr.fill() + #z += os*cr.text_extents(letter)[4] + + texttimer.stop() + del data + del layers + + timer.stop() + rendertimer.stop() + debug(self.bbox) + callback(True) - timer.stop() - rendertimer.stop() - debug(self.bbox) - callback(True) - class ImageLoader: - def __init__(self): - self.cache = {} - def __getitem__(self, url): - if url in self.cache: - return self.cache[url] - else: - print url, os_module.path.exists(url) - if os_module.path.exists(url): - self.cache[url] = cairo.ImageSurface.create_from_png (url) - return self.cache[url] - else: - return False \ No newline at end of file + def __init__(self): + self.cache = {} + def __getitem__(self, url): + if url in self.cache: + return self.cache[url] + else: + print url, os_module.path.exists(url) + if os_module.path.exists(url): + self.cache[url] = cairo.ImageSurface.create_from_png (url) + return self.cache[url] + else: + return False diff --git a/src/simple_wms.py b/src/simple_wms.py index 80bf6bb..48be4bc 100644 --- a/src/simple_wms.py +++ b/src/simple_wms.py @@ -40,10 +40,10 @@ db = DataBackend() try: - import psyco - psyco.full() + import psyco + psyco.full() except ImportError: - pass + pass OK = 200 ERROR = 500 @@ -63,8 +63,8 @@ urls = ( '/(.*)', 'mainhandler' ) class mainhandler: - def GET(self, crap): - return handler() + def GET(self, crap): + return handler() @@ -85,7 +85,7 @@ def twms_main(req): height=0 req_bbox = () if data.get("bbox",data.get("BBOX",None)): - req_bbox = tuple(map(float,data.get("bbox",data.get("BBOX",req_bbox)).split(","))) + req_bbox = tuple(map(float,data.get("bbox",data.get("BBOX",req_bbox)).split(","))) req_bbox = projections.to4326(req_bbox, srs) @@ -102,11 +102,10 @@ def twms_main(req): image_content = StringIO.StringIO() - + res.surface.write_to_png(image_content) - + resp = image_content.getvalue() return (OK, content_type, resp) - diff --git a/src/style.py b/src/style.py index 6572f77..a565e5c 100644 --- a/src/style.py +++ b/src/style.py @@ -23,136 +23,136 @@ from debug import debug from mapcss.webcolors.webcolors import whatever_to_cairo as colorparser class Styling(): - """ - Class used to choose the right way of rendering an object. - """ - def __init__(self, stylefile = None): - self.Selectors = {} - self.Selectors["way"] = [] - self.Selectors["node"] = [] - self.Selectors["relation"] = [] - if not stylefile: - # self.Selectors["way"].append(StyleSelector( ( [ ( ("building",),(None) ) ] ),{"fill-color": "#00f"} )) - - #if stylefile=="zzzz": - ### using "builtin" styling - self.Selectors["way"].append(StyleSelector( ( [ ( ("area",),("yes") ) ] ),{"fill-color": "#ff0000"} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),(None) ) ] ),{"width":1,"color":"#ff0000","text": "name", "text-position":"line","text-halo-radius":2,} )) - - self.Selectors["way"].append(StyleSelector( ( [ ( ("barrier",),(None) ) ] ),{"casing-width":1,} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),("residential", "tertiary", "living_street")) ] ),{"width": 3, "color":"#ffffff", "casing-width": 5, "z-index":10} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),("service", "unclassified")) ] ),{"width": 2.5, "color":"#ccc", "casing-width": 4, "z-index":9} )) - - self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),("primary", "motorway", "trunk")) ] ),{"width": 4, "color":"#ff0", "casing-width": 6, "z-index":11} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),("primary_link", "motorway_link", "trunk_link")) ] ),{"width": 3.5, "color":"#ff0", "casing-width": 6, "z-index":11} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),("secondary", )) ] ),{"width": 4, "color":"orange", "casing-width": 6, "z-index":10} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("living_street",),("yes")) ] ),{"width": 2, "casing-width": 3, "z-index": 0} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("landuse","natural"),("forest", "wood") ) ] ),{"fill-color": "#020"} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("landuse",),("industrial",) ) ] ),{"fill-color": "#855"} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("landuse",),("military",) ) ] ),{"fill-color": "pink"} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("waterway","natural"),("riverbank", "water") ) ] ),{"fill-color": "#002"} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("waterway","natural"),("river", "stream") ) ] ),{"color": "#002"} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("landuse","natural"),("grass",) ) ] ),{"fill-color": "#050",} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),("footway","pedestrian","path" )) ] ),{"width":2.5, "color":"#655", "dashes": [3,1],"z-index":3} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("bridge",),("yes") ) ] ),{"casing-width":10} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("power",),("line",)) ] ),{"width": 1, "color":"#ccc",} )) - self.Selectors["way"].append(StyleSelector( ( [ ( ("building",),(None) ) ] ),{"fill-color": "#522","text": "addr:housenumber","text-halo-radius":2,"z-index":100} ))#"extrude":10, - - self.stylefile = stylefile - self.useful_keys = set(["layer"]) - for objtype in self.Selectors.values(): # getting useful keys - for selector in objtype: - #debug(selector) - for tag in selector.tags: - self.useful_keys.update(set(tag[0])) - if "text" in selector.style: - self.useful_keys.update(set((selector.style["text"],))) - debug(self.useful_keys) - - - - def get_style(self, objtype, tags, nodata = False): """ - objtype is "node", "way" or "relation" - tags - object tags - nodata - we won't render that now, don't need exact styling + Class used to choose the right way of rendering an object. """ - resp = {} - for selector in self.Selectors[objtype]: - resp.update(selector.get_style(tags)) - if nodata: - if resp: - return True - if not nodata and resp: - #debug((tags, tags.get("layer",0)), ) - try: - resp["layer"] = int(tags.get("layer",0))*100+resp.get("z-index",0)+1000 - except ValueError: - resp["layer"] = 1000000 - - if "text" in resp: # unpacking text - if resp["text"] in tags: - resp["text"] = tags[resp["text"]] - #debug("text: %s"%resp["text"]) - else: - del resp["text"] - return resp - def filter_tags(self, tags): - """ - Returns only tags that are useful for rendering - """ - #resp = {} - #for k,v in tags.iteritems(): - # if k in self.useful_keys: - # resp[k] = v - return tags + def __init__(self, stylefile = None): + self.Selectors = {} + self.Selectors["way"] = [] + self.Selectors["node"] = [] + self.Selectors["relation"] = [] + if not stylefile: + # self.Selectors["way"].append(StyleSelector( ( [ ( ("building",),(None) ) ] ),{"fill-color": "#00f"} )) + + #if stylefile=="zzzz": + ### using "builtin" styling + self.Selectors["way"].append(StyleSelector( ( [ ( ("area",),("yes") ) ] ),{"fill-color": "#ff0000"} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),(None) ) ] ),{"width":1,"color":"#ff0000","text": "name", "text-position":"line","text-halo-radius":2,} )) + + self.Selectors["way"].append(StyleSelector( ( [ ( ("barrier",),(None) ) ] ),{"casing-width":1,} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),("residential", "tertiary", "living_street")) ] ),{"width": 3, "color":"#ffffff", "casing-width": 5, "z-index":10} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),("service", "unclassified")) ] ),{"width": 2.5, "color":"#ccc", "casing-width": 4, "z-index":9} )) + + self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),("primary", "motorway", "trunk")) ] ),{"width": 4, "color":"#ff0", "casing-width": 6, "z-index":11} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),("primary_link", "motorway_link", "trunk_link")) ] ),{"width": 3.5, "color":"#ff0", "casing-width": 6, "z-index":11} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),("secondary", )) ] ),{"width": 4, "color":"orange", "casing-width": 6, "z-index":10} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("living_street",),("yes")) ] ),{"width": 2, "casing-width": 3, "z-index": 0} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("landuse","natural"),("forest", "wood") ) ] ),{"fill-color": "#020"} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("landuse",),("industrial",) ) ] ),{"fill-color": "#855"} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("landuse",),("military",) ) ] ),{"fill-color": "pink"} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("waterway","natural"),("riverbank", "water") ) ] ),{"fill-color": "#002"} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("waterway","natural"),("river", "stream") ) ] ),{"color": "#002"} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("landuse","natural"),("grass",) ) ] ),{"fill-color": "#050",} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("highway",),("footway","pedestrian","path" )) ] ),{"width":2.5, "color":"#655", "dashes": [3,1],"z-index":3} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("bridge",),("yes") ) ] ),{"casing-width":10} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("power",),("line",)) ] ),{"width": 1, "color":"#ccc",} )) + self.Selectors["way"].append(StyleSelector( ( [ ( ("building",),(None) ) ] ),{"fill-color": "#522","text": "addr:housenumber","text-halo-radius":2,"z-index":100} ))#"extrude":10, + + self.stylefile = stylefile + self.useful_keys = set(["layer"]) + for objtype in self.Selectors.values(): # getting useful keys + for selector in objtype: + #debug(selector) + for tag in selector.tags: + self.useful_keys.update(set(tag[0])) + if "text" in selector.style: + self.useful_keys.update(set((selector.style["text"],))) + debug(self.useful_keys) + + + + def get_style(self, objtype, tags, nodata = False): + """ + objtype is "node", "way" or "relation" + tags - object tags + nodata - we won't render that now, don't need exact styling + """ + resp = {} + for selector in self.Selectors[objtype]: + resp.update(selector.get_style(tags)) + if nodata: + if resp: + return True + if not nodata and resp: + #debug((tags, tags.get("layer",0)), ) + try: + resp["layer"] = int(tags.get("layer",0))*100+resp.get("z-index",0)+1000 + except ValueError: + resp["layer"] = 1000000 + + if "text" in resp: # unpacking text + if resp["text"] in tags: + resp["text"] = tags[resp["text"]] + #debug("text: %s"%resp["text"]) + else: + del resp["text"] + return resp + def filter_tags(self, tags): + """ + Returns only tags that are useful for rendering + """ + #resp = {} + #for k,v in tags.iteritems(): + # if k in self.useful_keys: + # resp[k] = v + return tags + + - - class StyleSelector(): - def __init__(self, tags, style): - """ - Selector that decides if that style is right for the object - tags - list of tags [(("key","key"..), ("value", "value"...)), (("key","key"..), ("value", "value"...))] - style - MapCSS rules to apply - """ - self.tags = tags - self.style = {} - for key in style: - - keyz = key.lower() - - if "color" in keyz: - self.style[keyz] = colorparser(style[key]) - debug((colorparser(style[key]),style[key])) - else: - self.style[keyz] = style[key] - - def get_style(self, tags): - """ - Get actual styling for object. - """ - styled = False - #debug(self.tags) - for k,v in self.tags: - for j in k: - if j in tags: - if v: - if tags[j] in v: - styled = True - else: - styled = True - if styled: - return self.style - return {} - + def __init__(self, tags, style): + """ + Selector that decides if that style is right for the object + tags - list of tags [(("key","key"..), ("value", "value"...)), (("key","key"..), ("value", "value"...))] + style - MapCSS rules to apply + """ + self.tags = tags + self.style = {} + for key in style: + + keyz = key.lower() + + if "color" in keyz: + self.style[keyz] = colorparser(style[key]) + debug((colorparser(style[key]),style[key])) + else: + self.style[keyz] = style[key] + + def get_style(self, tags): + """ + Get actual styling for object. + """ + styled = False + #debug(self.tags) + for k,v in self.tags: + for j in k: + if j in tags: + if v: + if tags[j] in v: + styled = True + else: + styled = True + if styled: + return self.style + return {} + if __name__ == "__main__": - c = Styling() - print c.get_style("way", {"building":"yes"}) - print c.get_style("way", {"highway":"residential"}) - print c.get_style("way", {"highway":"road"}) - print c.get_style("way", {"highway":"residential", "building": "yes"}) - print c.get_style("way", {"highwadfgaay":"resifdgsdential", "builafgding": "yedfgs"}) - print c.get_style("way", {"highwadfgaay":"resifdgsdential", "builafgding": "yedfgs"}, True) - print c.get_style("way", {"highway":"residential", "building": "yes"}, True) - print c.filter_tags({"highwadfgaay":"resifdgsdential", "builafgding": "yedfgs", "building": "residential"}) \ No newline at end of file + c = Styling() + print c.get_style("way", {"building":"yes"}) + print c.get_style("way", {"highway":"residential"}) + print c.get_style("way", {"highway":"road"}) + print c.get_style("way", {"highway":"residential", "building": "yes"}) + print c.get_style("way", {"highwadfgaay":"resifdgsdential", "builafgding": "yedfgs"}) + print c.get_style("way", {"highwadfgaay":"resifdgsdential", "builafgding": "yedfgs"}, True) + print c.get_style("way", {"highway":"residential", "building": "yes"}, True) + print c.filter_tags({"highwadfgaay":"resifdgsdential", "builafgding": "yedfgs", "building": "residential"}) diff --git a/src/twms_fetcher.py b/src/twms_fetcher.py index ce435f9..47cca7c 100644 --- a/src/twms_fetcher.py +++ b/src/twms_fetcher.py @@ -37,87 +37,87 @@ metatiles_in_progress = {} renderlock = threading.Lock() def kothic_fetcher (z, x, y, this_layer): - if "max_zoom" in this_layer: - if z >= this_layer["max_zoom"]: - return None - bbox = projections.bbox_by_tile(z,x,y,"EPSG:3857") - db = DataBackend(path="/home/kom/osm/kothic/src/tiles") - res = RasterTile(256, 256, 1, db, "EPSG:3857") - res.update_surface(bbox, z, style) - f = NamedTemporaryFile() - f.close() - res.surface.write_to_png(f.name) - del res - del db - im = Image.open(f.name) - os.unlink(f.name) - im = im.convert("RGBA") + if "max_zoom" in this_layer: + if z >= this_layer["max_zoom"]: + return None + bbox = projections.bbox_by_tile(z,x,y,"EPSG:3857") + db = DataBackend(path="/home/kom/osm/kothic/src/tiles") + res = RasterTile(256, 256, 1, db, "EPSG:3857") + res.update_surface(bbox, z, style) + f = NamedTemporaryFile() + f.close() + res.surface.write_to_png(f.name) + del res + del db + im = Image.open(f.name) + os.unlink(f.name) + im = im.convert("RGBA") + + return im - return im - def kothic_metatile(z, x, y, this_layer): - - print z, x, y - global metatiles_in_progress - if "max_zoom" in this_layer: - if z >= this_layer["max_zoom"]: - return None - if z<5: - return None - - metatile_id = (z,int(x/8), int(y/8)) - try: - metatiles_in_progress[metatile_id].join() - except KeyError: - metatiles_in_progress[metatile_id] = threading.Thread(None, gen_metatile, None, (metatile_id, this_layer)) - metatiles_in_progress[metatile_id].start() - metatiles_in_progress[metatile_id].join() - except RuntimeError: - pass - - - local = config.tiles_cache + this_layer["prefix"] + "/z%s/%s/x%s/%s/y%s."%(z, x/1024, x, y/1024,y) - ext = this_layer["ext"] - if os.path.exists(local+ext): # First, look for tile in cache + print z, x, y + global metatiles_in_progress + if "max_zoom" in this_layer: + if z >= this_layer["max_zoom"]: + return None + if z<5: + return None + + metatile_id = (z,int(x/8), int(y/8)) + try: - im1 = Image.open(local+ext) - del metatiles_in_progress[metatile_id] - return im1 - except IOError: - os.remove(local+ext) + metatiles_in_progress[metatile_id].join() + except KeyError: + metatiles_in_progress[metatile_id] = threading.Thread(None, gen_metatile, None, (metatile_id, this_layer)) + metatiles_in_progress[metatile_id].start() + metatiles_in_progress[metatile_id].join() + except RuntimeError: + pass + + + local = config.tiles_cache + this_layer["prefix"] + "/z%s/%s/x%s/%s/y%s."%(z, x/1024, x, y/1024,y) + ext = this_layer["ext"] + if os.path.exists(local+ext): # First, look for tile in cache + try: + im1 = Image.open(local+ext) + del metatiles_in_progress[metatile_id] + return im1 + except IOError: + os.remove(local+ext) def gen_metatile(metatile_id, this_layer): - #renderlock.acquire() - z, x, y = metatile_id - z -= 3 - wh = 2560 - bb1 = projections.coords_by_tile(z, x-0.125, y-0.125, "EPSG:3857") - bb2 = projections.coords_by_tile(z, x+1.125, y+1.125, "EPSG:3857") - bbox = (bb1[0],bb2[1],bb2[0],bb1[1]) - db = DataBackend() - res = RasterTile(wh, wh, 1, db, "EPSG:3857") - res.update_surface(bbox, z+3, style) - f = NamedTemporaryFile() - f.close() - res.surface.write_to_png(f.name) - del res - del db - im = Image.open(f.name) - os.unlink(f.name) - im = im.convert("RGBA") - x*=8 - y*=8 - z+=3 - ext = this_layer["ext"] - for i in range(x,x+9): - for j in range(y,y+9): - local = config.tiles_cache + this_layer["prefix"] + "/z%s/%s/x%s/%s/y%s."%(z, i/1024, i, j/1024,j) - box = (256*(i-x+1),256*(j-y+1),256*(i-x+2),256*(j-y+2)) - im1 = im.crop(box) - if not os.path.exists("/".join(local.split("/")[:-1])): - os.makedirs("/".join(local.split("/")[:-1])) - im1.save(local+ext) - del im1 - #renderlock.release() + #renderlock.acquire() + z, x, y = metatile_id + z -= 3 + wh = 2560 + bb1 = projections.coords_by_tile(z, x-0.125, y-0.125, "EPSG:3857") + bb2 = projections.coords_by_tile(z, x+1.125, y+1.125, "EPSG:3857") + bbox = (bb1[0],bb2[1],bb2[0],bb1[1]) + db = DataBackend() + res = RasterTile(wh, wh, 1, db, "EPSG:3857") + res.update_surface(bbox, z+3, style) + f = NamedTemporaryFile() + f.close() + res.surface.write_to_png(f.name) + del res + del db + im = Image.open(f.name) + os.unlink(f.name) + im = im.convert("RGBA") + x*=8 + y*=8 + z+=3 + ext = this_layer["ext"] + for i in range(x,x+9): + for j in range(y,y+9): + local = config.tiles_cache + this_layer["prefix"] + "/z%s/%s/x%s/%s/y%s."%(z, i/1024, i, j/1024,j) + box = (256*(i-x+1),256*(j-y+1),256*(i-x+2),256*(j-y+2)) + im1 = im.crop(box) + if not os.path.exists("/".join(local.split("/")[:-1])): + os.makedirs("/".join(local.split("/")[:-1])) + im1.save(local+ext) + del im1 + #renderlock.release()