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")
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")
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 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.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 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 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 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()
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 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.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 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 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 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()
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) 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) er.stop() 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]))) # - 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)) 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) # - 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 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 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)
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) 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) er.stop() 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]))) # - 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)) 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) # - 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 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 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)