예제 #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")
예제 #2
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)