コード例 #1
0
 def test_get_paint_server(self):
     lg = LinearGradient()
     self.assertTrue(re.match("^url\(#id\d+\) none$",
                              lg.get_paint_server()))
     self.assertTrue(
         re.match("^url\(#id\d+\) red$",
                  lg.get_paint_server(default='red')))
コード例 #2
0
ファイル: test_gradients.py プロジェクト: behnam/typefacet
 def test_get_paint_server(self):
     lg = LinearGradient()
     self.assertTrue(re.match("^url\(#id\d+\) none$", lg.get_paint_server()))
     self.assertTrue(re.match("^url\(#id\d+\) red$", lg.get_paint_server(default='red')))
コード例 #3
0
ファイル: rankflow.py プロジェクト: p-charis/4cat
    def process(self):
        items = {}
        max_weight = 1
        colour_property = self.parameters.get(
            "colour_property", self.options["colour_property"]["default"])
        size_property = self.parameters.get(
            "size_property", self.options["size_property"]["default"])
        include_value = self.parameters.get("show_value", False)

        # first create a map with the ranks for each period
        weighted = False
        for row in self.iterate_items(self.source_file):
            if row["date"] not in items:
                items[row["date"]] = {}

            try:
                weight = float(row["value"])
                weighted = True
            except (KeyError, ValueError):
                weight = 1

            # Handle collocations a bit differently
            if "word_1" in row:
                # Trigrams
                if "word_3" in row:
                    label = row["word_1"] + " " + row["word_2"] + " " + row[
                        "word_3"]
                # Bigrams
                else:
                    label = row["word_1"] + " " + row["word_2"]
            else:
                label = row["item"]

            items[row["date"]][label] = weight
            max_weight = max(max_weight, weight)

        # determine per-period changes
        # this is used for determining what colour to give to nodes, and
        # visualise outlying items in the data
        changes = {}
        max_change = 1
        max_item_length = 0
        for period in items:
            changes[period] = {}
            for item in items[period]:
                max_item_length = max(len(item), max_item_length)
                now = items[period][item]
                then = -1
                for previous_period in items:
                    if previous_period == period:
                        break
                    for previous_item in items[previous_period]:
                        if previous_item == item:
                            then = items[previous_period][item]

                if then >= 0:
                    change = abs(now - then)
                    max_change = max(max_change, change)
                    changes[period][item] = change
                else:
                    changes[period][item] = 1

        # some sizing parameters for the chart - experiment with those
        fontsize_normal = 12
        fontsize_small = 8
        box_width = fontsize_normal
        box_height = fontsize_normal * 1.25  # boxes will never be smaller than this
        box_max_height = box_height * 10
        box_gap_x = max_item_length * fontsize_normal * 0.75
        box_gap_y = 5
        margin = 25

        # don't change this - initial X value for top left box
        box_start_x = margin

        # we use this to know if and where to draw the flow curve between a box
        # and its previous counterpart
        previous_boxes = {}
        previous = []

        # we need to store the svg elements before drawing them to the canvas
        # because we need to know what elements to draw before we can set the
        # canvas up for drawing to
        boxes = []
        labels = []
        flows = []
        definitions = []

        # this is the default colour for items (it's blue-ish)
        # we're using HSV, so we can increase the hue for more prominent items
        base_colour = [.55, .95, .95]
        max_y = 0

        # go through all periods and draw boxes and flows
        for period in items:
            # reset Y coordinate, i.e. start at top
            box_start_y = margin

            for item in items[period]:
                # determine weight (and thereby height) of this particular item
                weight = items[period][item]
                weight_factor = weight / max_weight
                height = int(max(box_height, box_max_height * weight_factor)
                             ) if size_property and weighted else box_height

                # colour ranges from blue to red
                change = changes[period][item]
                change_factor = 0 if not weighted or change <= 0 else (
                    changes[period][item] / max_change)
                colour = base_colour.copy()
                colour[0] += (1 - base_colour[0]) * (
                    weight_factor
                    if colour_property == "weight" else change_factor)

                # first draw the box
                box_fill = "rgb(%i, %i, %i)" % tuple(
                    [int(v * 255) for v in colorsys.hsv_to_rgb(*colour)])
                box = Rect(insert=(box_start_x, box_start_y),
                           size=(box_width, height),
                           fill=box_fill)
                boxes.append(box)

                # then the text label
                label_y = (box_start_y + (height / 2)) + 3
                label_value = "" if not include_value else (
                    " (%s)" % weight if weight != 1 else "")
                label = Text(text=(item + label_value),
                             insert=(box_start_x + box_width + box_gap_y,
                                     label_y))
                labels.append(label)

                # store the max y coordinate, which marks the SVG overall height
                max_y = max(max_y, (box["y"] + box["height"]))

                # then draw the flow curve, if the box was ranked in an earlier
                # period as well
                if item in previous:
                    previous_box = previous_boxes[item]

                    # create a gradient from the colour of the previous box for
                    # this item to this box's colour
                    colour_from = previous_box["fill"]
                    colour_to = box["fill"]

                    gradient = LinearGradient(start=(0, 0), end=(1, 0))
                    gradient.add_stop_color(offset="0%", color=colour_from)
                    gradient.add_stop_color(offset="100%", color=colour_to)
                    definitions.append(gradient)

                    # the addition of ' none' in the auto-generated fill colour
                    # messes up some viewers/browsers, so get rid of it
                    gradient_key = gradient.get_paint_server().replace(
                        " none", "")

                    # calculate control points for the connecting bezier bar
                    # the top_offset determines the 'steepness' of the curve,
                    # experiment with the "/ 2" part to make it less or more
                    # steep
                    top_offset = (box["x"] - previous_box["x"] +
                                  previous_box["width"]) / 2
                    control_top_left = (previous_box["x"] +
                                        previous_box["width"] + top_offset,
                                        previous_box["y"])
                    control_top_right = (box["x"] - top_offset, box["y"])

                    bottom_offset = top_offset  # mirroring looks best
                    control_bottom_left = (previous_box["x"] +
                                           previous_box["width"] +
                                           bottom_offset, previous_box["y"] +
                                           previous_box["height"])
                    control_bottom_right = (box["x"] - bottom_offset,
                                            box["y"] + box["height"])

                    # now add the bezier curves - svgwrite has no convenience
                    # function for beziers unfortunately. we're using cubic
                    # beziers though quadratic could work as well since our
                    # control points are, in principle, mirrored
                    flow_start = (previous_box["x"] + previous_box["width"],
                                  previous_box["y"])
                    flow = Path(fill=gradient_key, opacity="0.35")
                    flow.push("M %f %f" % flow_start)  # go to start
                    flow.push("C %f %f %f %f %f %f" %
                              (*control_top_left, *control_top_right, box["x"],
                               box["y"]))  # top bezier
                    flow.push(
                        "L %f %f" %
                        (box["x"], box["y"] + box["height"]))  # right boundary
                    flow.push("C %f %f %f %f %f %f" %
                              (*control_bottom_right, *control_bottom_left,
                               previous_box["x"] + previous_box["width"],
                               previous_box["y"] +
                               previous_box["height"]))  # bottom bezier
                    flow.push("L %f %f" % flow_start)  # back to start
                    flow.push("Z")  # close path

                    flows.append(flow)

                # mark this item as having appeared previously
                previous.append(item)
                previous_boxes[item] = box

                box_start_y += height + box_gap_y

            box_start_x += (box_gap_x + box_width)

        # generate SVG canvas to add elements to
        canvas = get_4cat_canvas(self.dataset.get_results_path(),
                                 width=(margin * 2) +
                                 (len(items) * (box_width + box_gap_x)),
                                 height=max_y + (margin * 2),
                                 fontsize_normal=fontsize_normal,
                                 fontsize_small=fontsize_small)

        # now add the various shapes and paths. We only do this here rather than
        # as we go because only at this point can the canvas be instantiated, as
        # before we don't know the dimensions of the SVG drawing.

        # add our gradients so they can be referenced
        for definition in definitions:
            canvas.defs.add(definition)

        # add flows (which should go beyond the boxes)
        for flow in flows:
            canvas.add(flow)

        # add boxes and labels:
        for item in (*boxes, *labels):
            canvas.add(item)

        # finally, save the svg file
        canvas.saveas(pretty=True,
                      filename=str(self.dataset.get_results_path()))
        self.dataset.finish(len(items) * len(list(items.items()).pop()))