Example #1
0
 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")
Example #2
0
 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")
Example #3
0
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()
Example #4
0
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()
Example #5
0
    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)
Example #6
0
  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)