[Image of Disney princesses]

Digital humanities


Maintained by: David J. Birnbaum (djbpitt@gmail.com) [Creative Commons BY-NC-SA 3.0 Unported License] Last modified: 2017-04-10T00:51:26+0000


SVG test

The task

Your first SVG homework assignment asked you to create by hand an SVG bar chart that included the results of a Best stooge ever contest, and your second and third SVG homework assignments asked you to write XSLT to generate a bar graph of presidential election results. You may have figured out that we faked the data for the Best stooge ever contest, but this one is for real: with the recent release of Disney’s live action Beauty and the Beast, Buzzfeed polled its readers about their favorite Disney princess. The poll’s results can be represented in a bar graph, like the data from the stooge contest or the US presidential election. Your task for this test is to write an XSLT stylesheet to convert the XML results of the princess poll (below) into an SVG bar graph. At a minimum, your graph should include the X and Y axes with labels (with whatever labels you consider appropriate), as well as bars for the percentage of votes for each princess. You should upload only your XSLT to CourseWeb; we do not need either the XML or the SVG that your XSLT generates.

In the XML below, each element contains the number of raw votes each princess received.

<results>
    <princess name="mulan">18400</princess>
    <princess name="ariel">24100</princess>
    <princess name="merida">5736</princess>
    <princess name="pocahontas">8621</princess>
    <princess name="aurora">9554</princess>
    <princess name="rapunzel">12900</princess>
    <princess name="cinderella">7537</princess>
    <princess name="elsa">6292</princess>
    <princess name="tiana">4512</princess>
    <princess name="belle">25800</princess>
    <princess name="snow_white">2824</princess>
    <princess name="anna">3756</princess>
    <princess name="jasmine">10500</princess>
</results>

Our Solution

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0"
    xmlns="http://www.w3.org/2000/svg">
    <xsl:output method="xml" indent="yes"/>
    <xsl:variable name="interbarSpace" select="$barWidth div 2" as="xs:integer"/>
    <xsl:variable name="barWidth" select="30" as="xs:integer"/>
    <xsl:variable name="yScale" as="xs:double" select="3"/>
    <xsl:variable name="color" as="xs:string+"
        select="'pink', 'CornflowerBlue', 'Violet', 'LemonChiffon', 'Lavender', 'LightCyan', 
            'LightGreen', 'pink', 'CornflowerBlue', 'Violet', 'LemonChiffon', 'Lavender', 
            'LightCyan'"/>
    <xsl:template match="/">
        <xsl:variable name="maxHeight" select="max(//princess) div sum(//princess) * 100"
            as="xs:double"/>
        <xsl:variable name="XLength" select="($interbarSpace + $barWidth) * count(//princess)"
            as="xs:integer"/>
        <svg width="{$XLength}" height="{$maxHeight + 250}">
            <g transform="translate(40,{$maxHeight + 200})">
                <!-- x-axis -->
                <line x1="0" y1="0" x2="{$XLength}" y2="0" stroke="black" stroke-width="2"/>
                <!-- y-axis -->
                <line x1="0" y1="0" x2="0" y2="-{$maxHeight * $yScale + 30}" stroke="black"
                    stroke-width="2"/>
                <!-- 10% gray line -->
                <line x1="0" y1="-{10 * $yScale}" x2="{$XLength}" y2="-{10 * $yScale}" stroke="gray"
                    stroke-width="1"/>
                <text x="-10" y="-{10 * $yScale}" text-anchor="middle" font-size="50%">10%</text>
                <!-- 20% gray line -->
                <line x1="0" y1="-{20 * $yScale}" x2="{$XLength}" y2="-{20 * $yScale}" stroke="gray"
                    stroke-width="1"/>
                <text x="-10" y="-{20 * $yScale}" text-anchor="middle" font-size="50%">20%</text>
                <!-- x-axis label -->
                <text x="-20" y="-{$maxHeight div 2 * $yScale}" font-size="65%" text-anchor="middle"
                    transform="rotate(270 -20, -{$maxHeight div 2 * $yScale})">Percentage of
                    Votes</text>
                <!-- y-axis label -->
                <text x="{$XLength div 2}" y="35" text-anchor="middle" font-size="75%"
                    >Princesses</text>
                <!-- title -->
                <text x="{$XLength div 2}" y="-{$maxHeight * $yScale + 25}" text-anchor="middle"
                    >Popularity of Disney Princesses</text>
                <xsl:apply-templates select="//princess"/>
            </g>
        </svg>
    </xsl:template>
    <xsl:template match="princess">
        <xsl:variable name="XPos"
            select="(position() - 1) * ($barWidth + $interbarSpace) + $interbarSpace"/>
        <xsl:variable name="votes" as="xs:double" select="."/>
        <xsl:variable name="total" as="xs:double" select="sum(//princess)"/>
        <xsl:variable name="votePercent" as="xs:double" select="$votes div $total"/>
        <!--bars-->
        <rect x="{$XPos}" y="-{$votePercent * 100 * $yScale}"
            height="{$votePercent * 100 * $yScale}" width="{$barWidth}" stroke="black"
            fill="{$color[count(current()/preceding-sibling::princess) + 1]}"/>
        <!--princess name-->
        <text x="{$XPos + ($barWidth div 2)}" y="20" text-anchor="middle" text-size="12pt"
            font-size="50%">
            <xsl:value-of select="translate(@name, '_', ' ')"/>
        </text>
        <!--percentage of votes text-->
        <text x="{$XPos + ($barWidth div 2)}" y="-{($votePercent * 100 * $yScale) + 5}"
            font-size="75%" text-anchor="middle"><xsl:value-of
                select="round-half-to-even(. div sum(//princess) * 100)"/>%</text>
    </xsl:template>
</xsl:stylesheet>

The SVG output of our transformation looks like:

10% 20% Percentage of Votes Princesses Popularity of Disney Princesses mulan 13% ariel 17% merida 4% pocahontas 6% aurora 7% rapunzel 9% cinderella 5% elsa 4% tiana 3% belle 18% snow white 2% anna 3% jasmine 7%

A robust solution to this task uses variables so that the XSLT will continue to work should the data set change. After the next Disney princess poll, the XSLT above will produce an SVG graph without much tweaking or adjusting, despite additions. The exception to this, however, is if any princess were to receive more than 25% of the vote, we would no longer be producing enough gray lines to make the graph look balanced. Based on the public’s divided opinions about princesses, we think our choice not to include more lines as the maximum height grows is a safe one. If we needed bullet-proof code, we could have found the maximum percentage and let the data tell us how high our ruling lines needed to go.

We chose to color our bars based on the position of the bar, although we could also have made them all the same color, or chosen a color according to the position after sorting by height, or simply assigned colors to princesses arbitrarily, but it was hard to ignore the way the spring-like pastels fit with the princess theme. Oh, and instead of repeating the colors in our list, we could have used the mod operator to ensure that we would cycle through them when we got to the end. Can you figure out how?

Finally, we used tbe translate() function to remove the underscore from Snow White’s name. You may have chosen to try to capitalize the first letter of each name, in which case Snow White’s name poses a unique challenge. If you’d like to see how to do that, again, make an appointment to see one of us and we’ll be happy to develop it with you.