ebeshero / DHClass-Hub

a repository to help introduce and orient students to the GitHub collaboration environment, and to support DH classes.
GNU Affero General Public License v3.0
27 stars 27 forks source link

XSLT Solution for SVG 2 #648

Closed NADGIT closed 5 years ago

NADGIT commented 5 years ago

So... Uh, here it is. The XPath works pretty much the same in XQuery, though, so you could 'translate' this into XQuery if you like

`<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:math="http://www.w3.org/2005/xpath-functions/math" exclude-result-prefixes="xs math" xmlns:exslt="http://exslt.org/common" xmlns="http://www.w3.org/2000/svg" version="3.0">

<xsl:output
    method="xml"
    indent="yes"
    standalone="no"
    doctype-public="-//W3C//DTD SVG 1.1//EN"
    doctype-system="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"
    media-type="image/svg" />

<xsl:variable name="banksyColl" as="document-node()+" select="collection('banksy_XML/?select=*.xml')"/>
<!-- This is how you make a variable in XSLT. They work very similar to XQuery's variables, the only issue being that they're a little more strongly typed since you can declare their type beforehand. -->
<xsl:variable name="dates" select="$banksyColl//sourceDesc//date/@when"/>

<xsl:variable name="years" select="$dates ! tokenize(.,'-')[1]"/>

<xsl:variable name="min" select="xs:integer(min($years))"/>

<xsl:variable name="max" select="xs:integer(max($years))"/>

<xsl:variable name="timelineSpacer" as="xs:integer" select="100"/>

<xsl:variable name="lineLength" select="$max - $min"/>

<xsl:template match="/">
    <svg width="2000" height="3000">
        <g transform= "translate(30,30)">
            <line x1="0" y1="0" x2="0" y2="{$lineLength * $timelineSpacer}" style="stroke:blue;stroke-width:2;"/>  

            <xsl:for-each select="0 to $lineLength">

                <xsl:variable name="yPos" as="xs:integer" select="$timelineSpacer * position() -$timelineSpacer"/>
                <!-- I use position() in the above line because because iterator variables don't exist in XSLT-->
                <xsl:variable name="currentYear" select="position() + 1998"/>

                <xsl:variable name="currentWorks" select="$banksyColl//sourceDesc//date[matches(@when, xs:string($currentYear))]"/>

                <xsl:variable name="workCount" as="xs:integer" select="count($currentWorks)"/>

                <xsl:variable name="graffitiCount" as="xs:integer" select="count($currentWorks/../medium[@type = 'spray_paint'])"/>

                <xsl:variable name="canvasCount" as="xs:integer" select="count($currentWorks/../medium[@type = 'canvas'])"/>

                <circle cx="0" cy="{$yPos}" r="{$workCount * 2}" stroke="purple" stroke-width="3" fill="violet"/>

                <text x="15" y="{$yPos}"><xsl:value-of select="$currentYear"/>: <xsl:apply-templates select="string-join($currentWorks/../title/text(), ', ')"/></text>

                <rect width="{20 * $graffitiCount}" height="15" x="20" y="{$yPos + 30}" fill="red"/>

                <rect width="{20 * $canvasCount}" height="15" x="{20 + (20 * $graffitiCount)}" y="{$yPos + 30}" fill="blue"/>

                <text x="{20 * xs:integer(($graffitiCount > 0))}" y="{$yPos + 44}" fill="white"><xsl:value-of select="$graffitiCount"/></text>

                <!-- I like to mess with datatypes: 
                    if the comparision evaluates to false, the "0" is converted to an int and teleports the text to the left side of the screen. 
                    Out of sight, out of mind! -->

                <text x="{20 + (20 * $graffitiCount)}" y="{$yPos + 44}" fill="white"><xsl:value-of select="$canvasCount"/></text>

            </xsl:for-each>
        </g>
    </svg>
</xsl:template>

</xsl:stylesheet>`

ebeshero commented 5 years ago

@NADGIT This is terrific—thank you! As for your comment on XSLT vs XQuery variables, you can set strong datatypes on the global kind of variable, but I think you just don’t type the FLWOR variables. Here is an example showing how to set a datatype on a global variable pointing to the Banksy collection for our SVG 2 Exercise, as a plural sequence of document nodes:

declare variable $banksyColl as document-node()+ := collection('/db/Assignments/banksyForSVG/');

You don’t have to set the datatype for your variables, but as @NADGIT is exemplifying here, it is always a good idea to use strong data typing where you can—and I really should be modeling this on my assignment page!