示例#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
 def test_add_stop_color(self):
     lg = LinearGradient()
     lg.add_stop_color(offset=0.5, color='red', opacity=1.0)
     self.assertEqual(
         lg.tostring(),
         '<linearGradient><stop offset="0.5" stop-color="red" stop-opacity="1.0" /></linearGradient>'
     )
示例#3
0
 def test_constructor(self):
     lg = LinearGradient(start=(1, 2),
                         end=(10, 20),
                         inherit='#test',
                         debug=True,
                         profile='full')
     self.assertEqual(
         '<linearGradient x1="1" x2="10" xlink:href="#test" y1="2" y2="20" />',
         lg.tostring())
示例#4
0
 def test_add_colors(self):
     lg = LinearGradient()
     lg.add_colors(['white', 'red', 'blue', 'green'], opacity=0.5)
     result = '<linearGradient>' \
              '<stop offset="0.0" stop-color="white" stop-opacity="0.5" />' \
              '<stop offset="0.333" stop-color="red" stop-opacity="0.5" />' \
              '<stop offset="0.667" stop-color="blue" stop-opacity="0.5" />' \
              '<stop offset="1.0" stop-color="green" stop-opacity="0.5" />' \
              '</linearGradient>'
     self.assertEqual(lg.tostring(), result)
示例#5
0
 def test_add_colors(self):
     lg = LinearGradient()
     lg.add_colors(['white', 'red', 'blue', 'green'], opacity=0.5)
     result = '<linearGradient>' \
              '<stop offset="0.0" stop-color="white" stop-opacity="0.5" />' \
              '<stop offset="0.333" stop-color="red" stop-opacity="0.5" />' \
              '<stop offset="0.667" stop-color="blue" stop-opacity="0.5" />' \
              '<stop offset="1.0" stop-color="green" stop-opacity="0.5" />' \
              '</linearGradient>'
     self.assertEqual(lg.tostring(), result)
示例#6
0
 def test_inherit(self):
     inherit_from = LinearGradient(id='test')
     lg = LinearGradient(inherit=inherit_from)
     self.assertTrue('<linearGradient xlink:href="#test"/>', lg.tostring())
示例#7
0
        try:
            return random.sample(colors[using_color][label], 1)[0]
        except KeyError:
            return random.sample(colors[using_color][label - 1], 1)[0]

    else:
        return random.sample(colors[using_color], 1)[0]


if have_gradient:
    defs = Defs(id='gradients')
    colors_gradient = []
    gradient_direction = [(1, 1), (0, 1), (1, 0)]
    for gradient_index in range(gradient_cnt):
        l_g = LinearGradient((0, 0),
                             random.choice(gradient_direction),
                             id='gradient_%d' % gradient_index)
        l_g.add_colors(random.sample(colors[using_color], 2))
        defs.add(l_g)
        colors_gradient.append('url(#gradient_%d)' % gradient_index)

    colors[using_color] += colors_gradient

    dwg.add(defs)

# start
x_p0 = []
for i in range(triangle_leftest + 1):
    x_p0.append(0)
if uni_left:
    y_p0 = []
示例#8
0
 def test_inherit(self):
     inherit_from = LinearGradient(id='test')
     lg = LinearGradient(inherit=inherit_from)
     self.assertTrue('<linearGradient xlink:href="#test"/>', lg.tostring())
示例#9
0
 def test_add_stop_color(self):
     lg = LinearGradient()
     lg.add_stop_color(offset=0.5, color='red', opacity=1.0)
     self.assertEqual(lg.tostring(), '<linearGradient><stop offset="0.5" stop-color="red" stop-opacity="1.0" /></linearGradient>')
示例#10
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')))
示例#11
0
 def test_constructor(self):
     lg = LinearGradient(start=(1, 2), end=(10, 20), inherit='#test', debug=True, profile='full')
     self.assertEqual(
         '<linearGradient x1="1" x2="10" xlink:href="#test" y1="2" y2="20" />',
         lg.tostring())
示例#12
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()))
示例#13
0
def grid(settings={}):
    # Check arguments
    for setting_name, setting_default in DEFAULT_SETTINGS_GRID.items():
        if setting_name not in settings.keys():
            settings[setting_name] = setting_default

    # Useful calculations
    # Bounds defined as (x0, y0, x1, y1)
    sizes = (min(settings['canvas_size']) // 50,
             min(settings['canvas_size']) // 40)
    margin = 2 * sizes[0]
    bar_width = 15 * sizes[0]
    tickbox_1_bounds = (margin, margin, settings['canvas_size'][0] * 0.45,
                        15 * sizes[0])
    tickbox_2_bounds = (settings['canvas_size'][0] * 0.55, margin,
                        settings['canvas_size'][0] - margin, 15 * sizes[0])
    divider_line_y = 22.5 * sizes[0]
    bar_charts_bounds = (margin + 3 * sizes[0], 28 * sizes[0],
                         settings['canvas_size'][0] - margin,
                         settings['canvas_size'][1] - 10 * sizes[0])
    bar_1_x = bar_charts_bounds[0] + (bar_charts_bounds[2] -
                                      bar_charts_bounds[0]) * 1 / 4
    bar_2_x = bar_charts_bounds[0] + (bar_charts_bounds[2] -
                                      bar_charts_bounds[0]) * 3 / 4

    # Initialise drawing
    dwg = svgwrite.Drawing(profile='full')

    # Set viewbox attribute for scaling
    dwg.attribs['viewBox'] = '0 0 ' + ' '.join(
        [str(x) for x in settings['canvas_size']])
    dwg.attribs['width'] = '95%'
    dwg.attribs['height'] = '95%'

    # Set HTML attributes for interaction
    # If the user doesn't set an ID for the chart, then it shouldn't have an ID defined in the XML. But, the svg_id variable still needs to be
    # defined for later, to make sure there's something to prepend to the IDs of any child elements that *do* need IDs.
    if settings['svg_id'] is None:
        settings['svg_id'] = 'grid'
    else:
        dwg.attribs['id'] = settings['svg_id']
    if settings['svg_hidden']:
        dwg.attribs['style'] = 'display: none;'

    # Checkboxes
    dwg.add(
        dwg.polygon(points=[(tickbox_1_bounds[0], tickbox_1_bounds[1]),
                            (tickbox_1_bounds[2], tickbox_1_bounds[1]),
                            (tickbox_1_bounds[2], tickbox_1_bounds[3]),
                            (tickbox_1_bounds[0], tickbox_1_bounds[3])],
                    fill=COLORS['VL_GREY'],
                    fill_opacity=0.8,
                    stroke=COLORS['BLACK'],
                    stroke_width=2,
                    stroke_opacity=1,
                    id='checkbox-1'))
    dwg.add(
        dwg.text('',
                 insert=((tickbox_1_bounds[0] + tickbox_1_bounds[2]) / 2,
                         (tickbox_1_bounds[1] + tickbox_1_bounds[3]) / 2),
                 font_size=2 * sizes[1],
                 font_family='Arial',
                 font_weight='bold',
                 fill=COLORS['BLACK'],
                 fill_opacity=1,
                 text_anchor='middle',
                 alignment_baseline='central',
                 id='checkbox-1-text'))
    dwg.add(
        dwg.text(settings['box_1_label'],
                 insert=((tickbox_1_bounds[0] + tickbox_1_bounds[2]) / 2,
                         tickbox_1_bounds[3] + 25),
                 font_size=1.8 * sizes[1],
                 font_family='Arial',
                 fill=COLORS['BLACK'],
                 fill_opacity=1,
                 text_anchor='middle',
                 alignment_baseline='central'))
    dwg.add(
        dwg.polygon(points=[(tickbox_2_bounds[0], tickbox_2_bounds[1]),
                            (tickbox_2_bounds[2], tickbox_2_bounds[1]),
                            (tickbox_2_bounds[2], tickbox_2_bounds[3]),
                            (tickbox_2_bounds[0], tickbox_2_bounds[3])],
                    fill=COLORS['VL_GREY'],
                    fill_opacity=0.8,
                    stroke=COLORS['BLACK'],
                    stroke_width=2,
                    stroke_opacity=1,
                    id='checkbox-2'))
    dwg.add(
        dwg.text('',
                 insert=((tickbox_2_bounds[0] + tickbox_2_bounds[2]) / 2,
                         (tickbox_2_bounds[1] + tickbox_2_bounds[3]) / 2),
                 font_size=2 * sizes[1],
                 font_family='Arial',
                 font_weight='bold',
                 fill=COLORS['BLACK'],
                 fill_opacity=1,
                 text_anchor='middle',
                 alignment_baseline='central',
                 id='checkbox-2-text'))
    dwg.add(
        dwg.text(settings['box_2_label'],
                 insert=((tickbox_2_bounds[0] + tickbox_2_bounds[2]) / 2,
                         tickbox_2_bounds[3] + 25),
                 font_size=1.8 * sizes[1],
                 font_family='Arial',
                 fill=COLORS['BLACK'],
                 fill_opacity=1,
                 text_anchor='middle',
                 alignment_baseline='central'))

    # Divider line
    dwg.add(
        dwg.line((margin, divider_line_y),
                 (settings['canvas_size'][0] - margin, divider_line_y),
                 stroke=COLORS['L_GREY'],
                 stroke_width=2))

    # Bars
    dwg.add(
        dwg.polygon(points=[(bar_charts_bounds[0], bar_charts_bounds[1]),
                            (bar_charts_bounds[2], bar_charts_bounds[1]),
                            (bar_charts_bounds[2], bar_charts_bounds[3]),
                            (bar_charts_bounds[0], bar_charts_bounds[3])],
                    fill=COLORS['WHITE'],
                    fill_opacity=0,
                    stroke=COLORS['BLACK'],
                    stroke_width=2,
                    stroke_opacity=1,
                    id='bar-charts-container'))
    dwg.add(
        dwg.text(settings['bar_label'],
                 insert=(settings['canvas_size'][0] / 2,
                         bar_charts_bounds[3] + 6 * sizes[0]),
                 font_size=1.8 * sizes[1],
                 font_family='Arial',
                 fill=COLORS['BLACK'],
                 fill_opacity=1,
                 text_anchor='middle',
                 alignment_baseline='central'))
    dwg.add(
        dwg.text(settings['bar_1_label'],
                 insert=(bar_1_x, bar_charts_bounds[3] + 20),
                 font_size=1.8 * sizes[1],
                 font_family='Arial',
                 fill=COLORS['BLACK'],
                 fill_opacity=1,
                 text_anchor='middle',
                 alignment_baseline='central'))
    dwg.add(
        dwg.text(settings['bar_2_label'],
                 insert=(bar_2_x, bar_charts_bounds[3] + 20),
                 font_size=1.8 * sizes[1],
                 font_family='Arial',
                 fill=COLORS['BLACK'],
                 fill_opacity=1,
                 text_anchor='middle',
                 alignment_baseline='central'))
    # Bar axis
    for i in range(10 + 1):
        height = bar_charts_bounds[1] + i * (bar_charts_bounds[3] -
                                             bar_charts_bounds[1]) / 10
        dwg.add(
            dwg.line((bar_charts_bounds[0] - 5, height),
                     (bar_charts_bounds[0] + 5, height),
                     stroke=COLORS['BLACK'],
                     stroke_width=2,
                     stroke_opacity=1))
        dwg.add(
            dwg.text(str(100 - i * 10),
                     insert=(bar_charts_bounds[0] - sizes[0], height),
                     font_size=1.5 * sizes[1],
                     font_family='Arial',
                     fill=COLORS['BLACK'],
                     fill_opacity=1,
                     text_anchor='end',
                     alignment_baseline='central'))
    # Bar 1
    dwg.add(
        dwg.polygon(points=[(bar_1_x - bar_width // 2, bar_charts_bounds[3]),
                            (bar_1_x - bar_width // 2, bar_charts_bounds[1]),
                            (bar_1_x + bar_width // 2, bar_charts_bounds[1]),
                            (bar_1_x + bar_width // 2, bar_charts_bounds[3])],
                    fill=COLORS['VL_GREY'],
                    fill_opacity=0.5,
                    stroke=COLORS['BLACK'],
                    stroke_width=2,
                    stroke_opacity=1))
    # Box plot 1
    box_plot_group_1 = dwg.g(id='boxplot-1', opacity=0)
    box_plot_group_1.add(
        dwg.polygon(points=[(bar_1_x - bar_width // 2, bar_charts_bounds[3]),
                            (bar_1_x - bar_width // 2, bar_charts_bounds[1]),
                            (bar_1_x + bar_width // 2, bar_charts_bounds[1]),
                            (bar_1_x + bar_width // 2, bar_charts_bounds[3])],
                    fill=COLORS['WHITE'],
                    fill_opacity=1,
                    stroke=COLORS['BLACK'],
                    stroke_width=2,
                    stroke_opacity=1))
    box_plot_group_1.add(
        dwg.polygon(points=[
            (bar_1_x - bar_width // 2, bar_charts_bounds[1] + 80),
            (bar_1_x - bar_width // 2, bar_charts_bounds[3] - 80),
            (bar_1_x + bar_width // 2, bar_charts_bounds[3] - 80),
            (bar_1_x + bar_width // 2, bar_charts_bounds[1] + 80)
        ],
                    fill='url(#gradient-1)',
                    fill_opacity=0.8,
                    stroke=COLORS['BLACK'],
                    stroke_width=2,
                    stroke_opacity=0.5,
                    id='boxplot-1-box'))
    gradient = LinearGradient(start=(0, 0), end=(0, 1), id='gradient-1')
    gradient.add_stop_color(offset='0%', color=COLORS['BAR_GREEN'])
    gradient.add_stop_color(offset='50%', color=COLORS['BAR_ORANGE'])
    gradient.add_stop_color(offset='100%', color=COLORS['BAR_RED'])
    dwg.defs.add(gradient)
    box_plot_group_1.add(
        dwg.line((bar_1_x - bar_width // 2, bar_charts_bounds[1] + 200),
                 (bar_1_x + bar_width // 2, bar_charts_bounds[1] + 200),
                 stroke=COLORS['BLACK'],
                 stroke_width=2,
                 stroke_opacity=0.5,
                 stroke_dasharray=2,
                 id='boxplot-1-line-high'))
    box_plot_group_1.add(
        dwg.line((bar_1_x - bar_width // 2,
                  (bar_charts_bounds[1] + bar_charts_bounds[3]) // 2),
                 (bar_1_x + bar_width // 2,
                  (bar_charts_bounds[1] + bar_charts_bounds[3]) // 2),
                 stroke=COLORS['BLACK'],
                 stroke_width=3,
                 stroke_opacity=0.8,
                 stroke_dasharray=5,
                 id='boxplot-1-line-mid'))
    box_plot_group_1.add(
        dwg.line((bar_1_x - bar_width // 2, bar_charts_bounds[3] - 200),
                 (bar_1_x + bar_width // 2, bar_charts_bounds[3] - 200),
                 stroke=COLORS['BLACK'],
                 stroke_width=2,
                 stroke_opacity=0.5,
                 stroke_dasharray=2,
                 id='boxplot-1-line-low'))
    box_plot_group_1.add(
        dwg.line((bar_1_x - bar_width // 2, bar_charts_bounds[3]),
                 (bar_1_x + bar_width // 2, bar_charts_bounds[3]),
                 fill_opacity=0,
                 stroke=COLORS['BLACK'],
                 stroke_width=4,
                 stroke_opacity=1,
                 id='bar-1-mainline'))
    box_plot_group_1.add(
        dwg.text('',
                 insert=(bar_1_x, bar_charts_bounds[3]),
                 font_size=2 * sizes[1],
                 font_family='Arial',
                 font_weight='bold',
                 fill=COLORS['BLACK'],
                 fill_opacity=1,
                 text_anchor='middle',
                 alignment_baseline='central',
                 id='bar-1-label'))
    dwg.add(box_plot_group_1)
    # Bar 2
    dwg.add(
        dwg.polygon(points=[(bar_2_x - bar_width // 2, bar_charts_bounds[3]),
                            (bar_2_x - bar_width // 2, bar_charts_bounds[1]),
                            (bar_2_x + bar_width // 2, bar_charts_bounds[1]),
                            (bar_2_x + bar_width // 2, bar_charts_bounds[3])],
                    fill=COLORS['VL_GREY'],
                    fill_opacity=0.5,
                    stroke=COLORS['BLACK'],
                    stroke_width=2,
                    stroke_opacity=1))
    # Box plot 2
    box_plot_group_2 = dwg.g(id='boxplot-2', opacity=0)
    box_plot_group_2.add(
        dwg.polygon(points=[(bar_2_x - bar_width // 2, bar_charts_bounds[3]),
                            (bar_2_x - bar_width // 2, bar_charts_bounds[1]),
                            (bar_2_x + bar_width // 2, bar_charts_bounds[1]),
                            (bar_2_x + bar_width // 2, bar_charts_bounds[3])],
                    fill=COLORS['WHITE'],
                    fill_opacity=1,
                    stroke=COLORS['BLACK'],
                    stroke_width=2,
                    stroke_opacity=1))
    box_plot_group_2.add(
        dwg.polygon(points=[
            (bar_2_x - bar_width // 2, bar_charts_bounds[1] + 80),
            (bar_2_x - bar_width // 2, bar_charts_bounds[3] - 80),
            (bar_2_x + bar_width // 2, bar_charts_bounds[3] - 80),
            (bar_2_x + bar_width // 2, bar_charts_bounds[1] + 80)
        ],
                    fill='url(#gradient-2)',
                    fill_opacity=0.8,
                    stroke=COLORS['BLACK'],
                    stroke_width=2,
                    stroke_opacity=0.5,
                    id='boxplot-2-box'))
    gradient = LinearGradient(start=(0, 0), end=(0, 1), id='gradient-2')
    gradient.add_stop_color(offset='0%', color=COLORS['BAR_GREEN'])
    gradient.add_stop_color(offset='50%', color=COLORS['BAR_ORANGE'])
    gradient.add_stop_color(offset='100%', color=COLORS['BAR_RED'])
    dwg.defs.add(gradient)
    box_plot_group_2.add(
        dwg.line((bar_2_x - bar_width // 2, bar_charts_bounds[1] + 200),
                 (bar_2_x + bar_width // 2, bar_charts_bounds[1] + 200),
                 stroke=COLORS['BLACK'],
                 stroke_width=2,
                 stroke_opacity=0.5,
                 stroke_dasharray=2,
                 id='boxplot-2-line-high'))
    box_plot_group_2.add(
        dwg.line((bar_2_x - bar_width // 2,
                  (bar_charts_bounds[1] + bar_charts_bounds[3]) // 2),
                 (bar_2_x + bar_width // 2,
                  (bar_charts_bounds[1] + bar_charts_bounds[3]) // 2),
                 stroke=COLORS['BLACK'],
                 stroke_width=3,
                 stroke_opacity=0.8,
                 stroke_dasharray=5,
                 id='boxplot-2-line-mid'))
    box_plot_group_2.add(
        dwg.line((bar_2_x - bar_width // 2, bar_charts_bounds[3] - 200),
                 (bar_2_x + bar_width // 2, bar_charts_bounds[3] - 200),
                 stroke=COLORS['BLACK'],
                 stroke_width=2,
                 stroke_opacity=0.5,
                 stroke_dasharray=2,
                 id='boxplot-2-line-low'))
    box_plot_group_2.add(
        dwg.line((bar_2_x - bar_width // 2, bar_charts_bounds[3]),
                 (bar_2_x + bar_width // 2, bar_charts_bounds[3]),
                 fill_opacity=0,
                 stroke=COLORS['BLACK'],
                 stroke_width=4,
                 stroke_opacity=1,
                 id='bar-2-mainline'))
    box_plot_group_2.add(
        dwg.text('',
                 insert=(bar_2_x, bar_charts_bounds[3]),
                 font_size=2 * sizes[1],
                 font_family='Arial',
                 font_weight='bold',
                 fill=COLORS['BLACK'],
                 fill_opacity=1,
                 text_anchor='middle',
                 alignment_baseline='central',
                 id='bar-2-label'))
    dwg.add(box_plot_group_2)

    return dwg.tostring()