<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:svg="http://www.w3.org/2000/svg"
                xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
                xmlns:dc="http://purl.org/dc/elements/1.1/"
                xmlns:dcterms="http://purl.org/dc/terms/"
                xmlns:xlink="http://www.w3.org/1999/xlink"
                version="1.0">

    <!-- 2003-08    DZ  Created.  Not robust, pretty or fast, but just used to
                        demonstrate that XSLT converts the input XML (RDF) into
                        a different format (SVG).

                        dan.zambonini@boxuk.com
    -->

    <xsl:output method="xml"
                omit-xml-declaration="yes"
                indent="yes"
                encoding="utf-8" />

    <xsl:variable name="rdf_copy" select="/rdf:RDF" />
    <xsl:variable name="default_tech_height">8</xsl:variable>      <!-- The default height for each technology box -->
    <xsl:variable name="default_tech_width">14</xsl:variable>      <!-- The default width for each technology box -->
    <xsl:variable name="default_tech_y_space">5</xsl:variable>     <!-- The number of units between each technology (vertically) -->
    <xsl:variable name="default_tech_x_space">7</xsl:variable>     <!-- The number of units between each technology (horizontally) -->
    <xsl:variable name="default_subject_y_space">28</xsl:variable> <!-- The number of units between each subject group (vertically) -->
    <xsl:variable name="x_subject_group">2</xsl:variable>          <!-- The x position for each 'subject' technology -->
    <xsl:variable name="x_confoms_group">80</xsl:variable>         <!-- The x position for each 'conforms' technology -->
    <xsl:variable name="line_part_style">marker-mid: url(#arrow); stroke: #FF0000; stroke-width: 0.2</xsl:variable>
    <xsl:variable name="line_conform_style">marker-mid: url(#arrow); stroke-dasharray: 0.5, 0.5; stroke: #999999; stroke-width: 0.2</xsl:variable>


    <xsl:template match="/rdf:RDF">
        <xsl:call-template name="create_svg_drawing" />
    </xsl:template>


    <xsl:template name="create_svg_drawing">
        <!-- Group technologies under 'subject' groupings. -->
        <xsl:variable name="tech_subjects">
            <xsl:call-template name="get_xml_subject_groups" />
        </xsl:variable>

        <!-- Find all technologies that are 'conformed to' -->
        <xsl:variable name="tech_conform">
            <xsl:call-template name="get_xml_conform_groups" />
        </xsl:variable>

        <!-- Find all technologies that are 'part of' another one -->
        <xsl:variable name="tech_parts">
            <xsl:call-template name="get_xml_part_groups" />
        </xsl:variable>

        <!-- Basic rules of diagram:
                a) show all 'subject groupings' on left.
                b) show all technologies that are 'conformed to' on right.
                c) show all 'part of' to the right of each technology
                d) position each technology under the other
                e) the 'height' of each technology will depend on the number
                   of technologies that it has as parts, OR the number that it
                   is part of (whichever is the most)
                f) 'conforms to' relationships displayed as dashed arrows
                g) 'part of' relationships displayed as solid arrows
        -->

        <!-- Give all 'conforms to' technologies positions on graph -->
        <xsl:variable name="tech_conform_with_pos">
            <xsl:call-template name="give_tech_conform_positions">
                <xsl:with-param name="tech_conform" select="$tech_conform" />
            </xsl:call-template>
        </xsl:variable>

        <!-- Give all 'subject' technologies positions on graph -->
        <xsl:variable name="tech_subjects_with_pos">
            <xsl:call-template name="give_tech_subject_positions">
                <xsl:with-param name="tech_subjects" select="$tech_subjects" />
            </xsl:call-template>
        </xsl:variable>

        <!-- Give all 'part' technologies positions on graph -->
        <xsl:variable name="tech_parts_with_pos">
            <xsl:call-template name="recursively_give_parts_positions">
                <xsl:with-param name="tech_parts" select="$tech_parts" />
                <xsl:with-param name="tech_with_pos">
                    <xsl:for-each select="$tech_subjects_with_pos/subject/rdf:Description">
                        <xsl:copy-of select="." />
                    </xsl:for-each>
                    <xsl:for-each select="$tech_conform_with_pos/rdf:Description">
                        <xsl:copy-of select="." />
                    </xsl:for-each>
                </xsl:with-param>
            </xsl:call-template>
        </xsl:variable>

        <!-- Create a main variable that contains all the technology rdfs WITH positions -->
        <xsl:variable name="tech_all_with_pos">
            <xsl:for-each select="$tech_subjects_with_pos/subject/rdf:Description">
                <xsl:copy-of select="." />
            </xsl:for-each>
            <xsl:for-each select="$tech_conform_with_pos/rdf:Description">
                <xsl:copy-of select="." />
            </xsl:for-each>
            <xsl:for-each select="$tech_parts_with_pos/rdf:Description">
                <xsl:copy-of select="." />
            </xsl:for-each>
        </xsl:variable>

        <!-- Draw graph -->
        <svg:svg width="20cm" height="14.5cm" viewBox="-30 -5 170 140">

           <!-- Define arrow -->
           <svg:defs>
              <svg:marker id="arrow" refX="0.5" refY="0.5" markerUnits="userSpaceOnUse" markerWidth="1" markerHeight="1" orient="auto" overflow="visible">
                <svg:path d="M0 0 1 0.5 0 1z" />
              </svg:marker>
           </svg:defs>

            <!-- Draw all subject technologies -->
            <xsl:for-each select="$tech_subjects_with_pos/subject">
                <!-- Draw subject boundary boxes -->
                <xsl:call-template name="draw_subject_boxes">
                    <xsl:with-param name="tech_all_with_pos" select="$tech_all_with_pos" />
                </xsl:call-template>

                <xsl:for-each select="rdf:Description">
                    <xsl:call-template name="draw_technology">
                        <xsl:with-param name="tech_all_with_pos" select="$tech_all_with_pos" />
                    </xsl:call-template>
                </xsl:for-each>
            </xsl:for-each>

            <!-- Draw all conforms technologies -->
            <xsl:for-each select="$tech_conform_with_pos/rdf:Description">
                <xsl:call-template name="draw_technology">
                    <xsl:with-param name="tech_all_with_pos" select="$tech_all_with_pos" />
                </xsl:call-template>
            </xsl:for-each>

            <!-- Draw all 'parts' -->
            <xsl:for-each select="$tech_parts_with_pos/rdf:Description">
                <xsl:call-template name="draw_technology">
                    <xsl:with-param name="tech_all_with_pos" select="$tech_all_with_pos" />
                </xsl:call-template>
            </xsl:for-each>

            <!-- Draw key for graph -->
            <xsl:call-template name="draw_graph_key" />

        </svg:svg>
    </xsl:template>


    <xsl:template name="draw_graph_key">
        <!-- We have 'reserved' any units sub x=0 for the key, main graph will only use x >= 0 -->

        <svg:rect x="-30" y="0" width="22" height="17" style="fill: #FFEEDD; stroke: #999999; stroke-width: 0.2" />

        <svg:text x="-28" y="3" style="font-size: 2; font-weight: bold">Key</svg:text>

        <svg:path d="M -28 9  L -25 9  L -22 9" style="{$line_part_style}"  />
        <svg:path d="M -28 14 L -25 14 L -22 14" style="{$line_conform_style}"  />
        <svg:text x="-20" y="10" style="font-size: 1.8">Has part</svg:text>
        <svg:text x="-20" y="15" style="font-size: 1.8">Conforms to</svg:text>
    </xsl:template>


    <xsl:template name="draw_subject_boxes">
        <xsl:param name="tech_all_with_pos" />

        <xsl:variable name="this_subject" select="@name" />

        <!-- get rdf descriptions for all technologies in this subject, including parts -->
        <xsl:variable name="tech_x_and_y_values">
            <xsl:call-template name="get_tech_x_and_y_values">
                <xsl:with-param name="examine_techs">
                    <xsl:for-each select="$tech_all_with_pos/rdf:Description[dc:Subject = $this_subject]">
                        <xsl:copy-of select="." />
                    </xsl:for-each>
                </xsl:with-param>
                <xsl:with-param name="tech_all_with_pos" select="$tech_all_with_pos" />
            </xsl:call-template>
        </xsl:variable>

        <!-- Calculate minimum and maximum x, y of techs in this subject -->
        <xsl:variable name="min_x">
            <xsl:for-each select="$tech_x_and_y_values/rdf:Description">
                <xsl:sort select="xpos" order="ascending" data-type="number" />
                <xsl:if test="position() = 1">
                    <xsl:value-of select="xpos" />
                </xsl:if>
            </xsl:for-each>
        </xsl:variable>

        <xsl:variable name="max_x">
            <xsl:for-each select="$tech_x_and_y_values/rdf:Description">
                <xsl:sort select="xpos + width" order="descending" data-type="number" />
                <xsl:if test="position() = 1">
                    <xsl:value-of select="xpos + width" />
                </xsl:if>
            </xsl:for-each>
        </xsl:variable>

        <xsl:variable name="min_y">
            <xsl:for-each select="$tech_x_and_y_values/rdf:Description">
                <xsl:sort select="ypos" order="ascending" data-type="number" />
                <xsl:if test="position() = 1">
                    <xsl:value-of select="ypos" />
                </xsl:if>
            </xsl:for-each>
        </xsl:variable>

        <xsl:variable name="max_y">
            <xsl:for-each select="$tech_x_and_y_values/rdf:Description">
                <xsl:sort select="ypos + height" order="descending" data-type="number" />
                <xsl:if test="position() = 1">
                    <xsl:value-of select="ypos + height" />
                </xsl:if>
            </xsl:for-each>
        </xsl:variable>

        <!-- Draw subject box -->
        <xsl:variable name="subject_gap">1.5</xsl:variable>
        <xsl:variable name="label_height">3</xsl:variable>

        <!-- Give it a little extra height, so we can label it underneath -->
        <svg:rect x="{$min_x - $subject_gap}" y="{$min_y - $subject_gap}" width="{($max_x - $min_x) + ($subject_gap * 2)}" height="{($max_y - $min_y) + ($subject_gap * 2) + $label_height}" style="stroke: #CCCCCC; stroke-width: 0.1; fill: #DDEEFF" />

        <!-- Label for subject -->
        <svg:text x="{$min_x - $subject_gap + 1}" y="{$max_y + $subject_gap + $label_height - 1}" style="font-size: 1.75;"><xsl:value-of select="$this_subject" /></svg:text>
    </xsl:template>


    <xsl:template name="get_tech_x_and_y_values">
        <xsl:param name="examine_techs" />
        <xsl:param name="tech_all_with_pos" />

        <xsl:for-each select="$examine_techs/rdf:Description">

            <xsl:copy-of select="." />

            <!-- also recurse for any 'parts' -->

            <xsl:for-each select="dcterms:hasPart">
                <xsl:variable name="part_uri" select="@rdf:resource" />
                <xsl:call-template name="get_tech_x_and_y_values">
                    <xsl:with-param name="examine_techs">
                        <xsl:for-each select="$tech_all_with_pos/rdf:Description[@rdf:about = $part_uri]">
                            <xsl:copy-of select="." />
                        </xsl:for-each>
                    </xsl:with-param>
                    <xsl:with-param name="tech_all_with_pos" select="$tech_all_with_pos" />
                </xsl:call-template>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:template>


    <xsl:template name="get_xml_part_groups">
        <xsl:for-each select="rdf:Description[@rdf:about = $rdf_copy/rdf:Description/dcterms:hasPart/@rdf:resource]">
            <xsl:copy-of select="." />
        </xsl:for-each>
    </xsl:template>


    <xsl:template name="recursively_give_parts_positions">
        <xsl:param name="tech_parts" />
        <xsl:param name="tech_with_pos" />

        <!-- Combine all tech_parts with positions and other technologies with positions -->
        <xsl:variable name="all_with_pos">
            <xsl:for-each select="$tech_with_pos/rdf:Description">
                <xsl:copy-of select="." />
            </xsl:for-each>

            <xsl:for-each select="$tech_parts/rdf:Description[width &gt; 0]">
                <xsl:copy-of select="." />
            </xsl:for-each>
        </xsl:variable>

        <xsl:choose>
            <!-- Have all parts got positions ?-->
            <xsl:when test="count($tech_parts/rdf:Description[not(width)]) = 0">
                <xsl:copy-of select="$tech_parts" />
            </xsl:when>
            <!-- Otherwise, call the template which will re-call this one, giving any technologies
                 it can a position -->
            <xsl:otherwise>
                <xsl:call-template name="give_tech_part_positions">
                    <xsl:with-param name="tech_parts"    select="$tech_parts"    />
                    <xsl:with-param name="tech_with_pos" select="$all_with_pos" />
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>


    <xsl:template name="give_tech_part_positions">
        <xsl:param name="tech_parts" />     <!-- all 'part of' technologies, to assign positions to -->
        <xsl:param name="tech_with_pos" />  <!-- all other technologies, which have already been assigned positions -->

        <xsl:variable name="new_tech_parts">
            <xsl:for-each select="$tech_parts/rdf:Description">

                <xsl:variable name="this_uri" select="@rdf:about" />

                <!-- First, check if it is 'part' of any technology that has not yet
                     been assigned a position (i.e. another 'part of' technology).
                     If so, then don't try to set it this time, the XSL will keep on
                     recursing until they are all set -->
                <xsl:variable name="num_part_of"          select="count($rdf_copy/rdf:Description[dcterms:hasPart/@rdf:resource = $this_uri])" />
                <xsl:variable name="num_part_of_with_pos" select="count($tech_with_pos/rdf:Description[dcterms:hasPart/@rdf:resource = $this_uri])" />

                <xsl:choose>
                    <xsl:when test="($num_part_of = $num_part_of_with_pos) and not(width)">

                        <!-- Calculate x and y for 'part' technology.

                             x = For the technologies that it is 'part' of, find the one with
                                 the greatest X.  Then, the x of this part will be given by
                                 that one's x, plus it's width, plus the x_spacing between
                                 technologies.

                             y = For the technologies that it is 'part' of, find the one with
                                 the smallest Y.  Then, the y of this part will be the same
                                 as that.  However, if this parent has more than one 'part',
                                 then each part in turn will need to take account of the previous
                                 parts (i.e. which will appear above them).
                        -->

                        <xsl:variable name="part_x_pos">
                            <xsl:for-each select="$tech_with_pos/rdf:Description[dcterms:hasPart/@rdf:resource = $this_uri]">
                                <xsl:sort select="xpos" order="descending" data-type="number" />

                                <xsl:if test="position() = 1">
                                    <xsl:value-of select="xpos + width + $default_tech_x_space" />
                                </xsl:if>
                            </xsl:for-each>
                        </xsl:variable>

                        <xsl:variable name="part_y_pos">
                            <xsl:for-each select="$tech_with_pos/rdf:Description[dcterms:hasPart/@rdf:resource = $this_uri]">
                                <xsl:sort select="ypos" order="ascending" data-type="number" />

                                <xsl:if test="position() = 1">
                                    <xsl:value-of select="ypos" />

                                </xsl:if>
                            </xsl:for-each>
                        </xsl:variable>

                        <xsl:variable name="extra_y_for_multi_parts">
                            <xsl:for-each select="$tech_with_pos/rdf:Description[dcterms:hasPart/@rdf:resource = $this_uri]">
                                <xsl:sort select="ypos" order="ascending" data-type="number" />

                                <xsl:if test="position() = 1">
                                    <xsl:choose>
                                        <xsl:when test="count(dcterms:hasPart) &gt; 1">
                                            <!-- Not robust, but assume each previous part is just standard/default height -->
                                            <xsl:for-each select="dcterms:hasPart">
                                                <xsl:if test="@rdf:resource = $this_uri">
                                                    <xsl:variable name="part_pos" select="position() - 1" />

                                                    <xsl:value-of select="($part_pos * $default_tech_height) + ($part_pos * $default_tech_y_space)" />
                                                </xsl:if>
                                            </xsl:for-each>
                                        </xsl:when>
                                        <xsl:otherwise>0</xsl:otherwise>
                                    </xsl:choose>
                                </xsl:if>
                            </xsl:for-each>
                        </xsl:variable>

                        <!-- re-output the rdf description for this technology, but with additional elements for svg positions -->
                        <xsl:call-template name="update_rdf_with_graph_values">
                            <xsl:with-param name="x_pos"  select="$part_x_pos"/>
                            <xsl:with-param name="y_pos"  select="$part_y_pos + $extra_y_for_multi_parts"/>
                            <xsl:with-param name="width"  select="$default_tech_width" />
                            <xsl:with-param name="height">
                                <xsl:call-template name="get_tech_height" />
                            </xsl:with-param>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy-of select="." />
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
        </xsl:variable>

        <!-- Now call back into recursive loop with other template -->
        <xsl:call-template name="recursively_give_parts_positions">
            <xsl:with-param name="tech_parts"    select="$new_tech_parts" />
            <xsl:with-param name="tech_with_pos" select="$tech_with_pos" />
        </xsl:call-template>
    </xsl:template>


    <xsl:template name="draw_technology">
        <xsl:param name="tech_all_with_pos" />
        <xsl:param name="xpos"     select="xpos" />
        <xsl:param name="ypos"     select="ypos" />
        <xsl:param name="width"    select="width" />
        <xsl:param name="height"   select="height" />

        <xsl:variable name="this_rdf" select="." />

        <!-- 'Drop Shadow' for technology -->
        <svg:rect x="{$xpos + 1}" y="{$ypos + 1}" width="{$width}" height="{$height}" style="fill: #CCCCCC" />

        <!-- Main box for technology -->
        <svg:rect x="{$xpos}" y="{$ypos}" width="{$width}" height="{$height}" style="stroke: black; stroke-width: 0.2;  fill: #99CCFF" />

        <!-- Label for technology name -->
        <svg:text x="{$xpos + ($width div 2)}" y="{$ypos + ($height div 2)}" style="text-anchor: middle; font-size: 2;"><xsl:value-of select="dc:Title" /></svg:text>

        <!-- Draw arrows to any 'part' relations -->
        <xsl:for-each select="dcterms:hasPart">
            <xsl:variable name="part_uri" select="@rdf:resource" />
            <xsl:variable name="part_rdf" select="$tech_all_with_pos/rdf:Description[@rdf:about = $part_uri]" />

            <xsl:call-template name="connect_rdfs">
                <xsl:with-param name="from_rdf" select="$this_rdf"    />
                <xsl:with-param name="to_rdf"   select="$part_rdf" />
                <xsl:with-param name="line_style" select="$line_part_style" />
            </xsl:call-template>
        </xsl:for-each>

        <!-- Draw arrows to any 'conforms' relations -->
        <xsl:for-each select="dcterms:conformsTo">
            <xsl:variable name="conforms_uri" select="@rdf:resource" />
            <xsl:variable name="conform_rdf"  select="$tech_all_with_pos/rdf:Description[@rdf:about = $conforms_uri]" />

            <xsl:call-template name="connect_rdfs">
                <xsl:with-param name="from_rdf" select="$this_rdf"    />
                <xsl:with-param name="to_rdf"   select="$conform_rdf" />
                <xsl:with-param name="line_style" select="$line_conform_style" />
            </xsl:call-template>
        </xsl:for-each>

    </xsl:template>


    <xsl:template name="connect_rdfs">
        <xsl:param name="from_rdf" />
        <xsl:param name="to_rdf" />
        <xsl:param name="line_style" />

        <!-- Connector lines will usually travel from right of the 'from' technology
             to the left of the 'to' technology'. However, if the from is below the
             above, then use top/bottom surfaces -->

        <!-- Choose what type of connector (top to bottom, left to right) we are using.
             In values below, top/bottom/left etc represent the surface we are attaching from/to.

             If we wanted to, could extend this to include bettern connectors for various conditions.
         -->
        <xsl:variable name="connector_type">
            <xsl:choose>
                <xsl:when test="($from_rdf/xpos &gt;= $to_rdf/xpos) and ($from_rdf/ypos &gt;= $to_rdf/ypos)">
                    <xsl:text>top-to-bottom</xsl:text>
                </xsl:when>
                <xsl:when test="($from_rdf/xpos &gt;= $to_rdf/xpos) and ($from_rdf/ypos &lt; $to_rdf/ypos)">
                    <xsl:text>bottom-to-top</xsl:text>
                </xsl:when>
                <xsl:when test="($from_rdf/xpos &lt; $to_rdf/xpos) and ($to_rdf/ypos &lt;= $from_rdf/ypos) and (($to_rdf/ypos + $to_rdf/height) &gt;= ($from_rdf/ypos + $from_rdf/height))">
                    <xsl:text>right-to-left-straight</xsl:text>
                </xsl:when>
                <xsl:when test="($from_rdf/xpos &lt; $to_rdf/xpos)">
                    <xsl:text>right-to-left</xsl:text>
                </xsl:when>
                <!-- could add other types in here -->
            </xsl:choose>
        </xsl:variable>

        <xsl:variable name="connector_gap">0.5</xsl:variable> <!-- a gap to leave before and after each technology, so connector never hits box of tech -->

        <xsl:variable name="from_x">
            <xsl:choose>
                <xsl:when test="($connector_type = 'right-to-left') or ($connector_type = 'right-to-left-straight')">
                    <xsl:value-of select="$from_rdf/xpos + $from_rdf/width + $connector_gap" />
                </xsl:when>
                <xsl:when test="$connector_type = 'top-to-bottom'">
                    <xsl:value-of select="$from_rdf/xpos + ($from_rdf/width div 2)" />
                </xsl:when>
            </xsl:choose>
        </xsl:variable>

        <xsl:variable name="from_y">
            <xsl:choose>
                <xsl:when test="($connector_type = 'right-to-left') or ($connector_type = 'right-to-left-straight')">
                    <xsl:value-of select="$from_rdf/ypos + ($from_rdf/height div 2)" />
                </xsl:when>
                <xsl:when test="$connector_type = 'top-to-bottom'">
                    <xsl:value-of select="$from_rdf/ypos - $connector_gap" />
                </xsl:when>
            </xsl:choose>
        </xsl:variable>

        <xsl:variable name="to_x">
            <xsl:choose>
                <xsl:when test="($connector_type = 'right-to-left') or ($connector_type = 'right-to-left-straight')">
                    <xsl:value-of select="$to_rdf/xpos - $connector_gap" />
                </xsl:when>
                <xsl:when test="$connector_type = 'top-to-bottom'">
                    <xsl:value-of select="$to_rdf/xpos + ($to_rdf/width div 2)" />
                </xsl:when>
            </xsl:choose>
        </xsl:variable>

        <xsl:variable name="to_y">
            <xsl:choose>
                <xsl:when test="$connector_type = 'right-to-left-straight'">
                    <xsl:value-of select="$from_y" />
                </xsl:when>
                <xsl:when test="$connector_type = 'right-to-left'">
                    <xsl:value-of select="$to_rdf/ypos + ($to_rdf/height div 2)" />
                </xsl:when>
                <xsl:when test="$connector_type = 'top-to-bottom'">
                    <xsl:value-of select="$to_rdf/ypos + $to_rdf/height + $connector_gap" />
                </xsl:when>
            </xsl:choose>
        </xsl:variable>

        <!-- Although all connector lines are straight, need to define the path in three steps,
             so that the 'marker-mid' will take effect, and display an arrow in the line -->
        <xsl:variable name="svg_path_coords">
            <xsl:text>M </xsl:text>
            <xsl:value-of select="$from_x" /><xsl:text> </xsl:text>
            <xsl:value-of select="$from_y" /><xsl:text> </xsl:text>
            <xsl:text>l </xsl:text>
            <xsl:value-of select="($to_x - $from_x) div 2" /><xsl:text> </xsl:text>
            <xsl:value-of select="($to_y - $from_y) div 2" /><xsl:text> </xsl:text>
            <xsl:text>L </xsl:text>
            <xsl:value-of select="$to_x" /><xsl:text> </xsl:text>
            <xsl:value-of select="$to_y" />
        </xsl:variable>

        <svg:path d="{$svg_path_coords}" style="{$line_style}"  />

    </xsl:template>


    <xsl:template name="give_tech_subject_positions">
        <xsl:param name="tech_subjects" />
        <xsl:param name="current_tech_pos">1</xsl:param>
        <xsl:param name="current_subject_pos">1</xsl:param>

        <xsl:variable name="num_subjects" select="count($tech_subjects/subject)" />                                       <!-- Number of subjects -->
        <xsl:variable name="num_techs"    select="count($tech_subjects/subject[$current_subject_pos]/rdf:Description)" /> <!-- Number of technologies IN CURRENT subject -->

        <!-- Recurse over each subject, then technology, giving graphical values
             to each in turn. -->

        <xsl:variable name="new_tech_subjects">
            <xsl:for-each select="$tech_subjects/subject">

                <xsl:variable name="tech_this_subject" select="." />

                <!-- Only process the current subject -->
                <xsl:choose>
                    <xsl:when test="position() = $current_subject_pos">
                        <subject name="{@name}">

                            <!-- calculate the y_offset for this subject, based on the total height of previous subjects -->
                            <xsl:variable name="subject_y_offset">
                                <xsl:choose>
                                    <xsl:when test="$current_subject_pos = 1">0</xsl:when>
                                    <xsl:otherwise>
                                        <!-- y-offset = for previous subject (ypos of LAST tech + its height)] + subject_spacing -->

                                        <!-- use for-each, as although we're only going to loop over 1 technology, easier to change context -->
                                        <xsl:for-each select="$tech_subjects/subject[$current_subject_pos - 1]/rdf:Description[last()]">
                                            <xsl:value-of select="number(ypos) + number(height) + $default_subject_y_space" />
                                        </xsl:for-each>
                                    </xsl:otherwise>
                                </xsl:choose>
                            </xsl:variable>

                            <xsl:for-each select="$tech_this_subject/rdf:Description">
                               <xsl:choose>
                                    <!-- Only work out values for the current technology -->
                                    <xsl:when test="position() = $current_tech_pos">

                                        <!-- 1. Get y position -->

                                        <xsl:variable name="y_pos">
                                            <xsl:choose>
                                                <xsl:when test="position() = 1"><xsl:value-of select="$subject_y_offset" /></xsl:when>
                                                <xsl:otherwise>
                                                    <!-- Get all previous heights -->
                                                    <xsl:variable name="sum_prev_height" select="sum($tech_this_subject/rdf:Description[position() &lt; $current_tech_pos]/height)" />

                                                    <!-- y pos = all previous heights, plus all spacing between all previous, plus spacing for this one -->
                                                    <xsl:value-of select="$sum_prev_height + ($default_tech_y_space * ($current_tech_pos - 1)) + $subject_y_offset" />
                                                </xsl:otherwise>
                                            </xsl:choose>
                                        </xsl:variable>

                                        <!-- 2. Get height -->
                                        <xsl:variable name="y_height">
                                            <xsl:call-template name="get_tech_height" />
                                        </xsl:variable>

                                        <!-- 3. width and x pos will always be the same, so can now update the RDF description -->
                                        <xsl:call-template name="update_rdf_with_graph_values">
                                            <xsl:with-param name="x_pos"  select="$x_subject_group" />
                                            <xsl:with-param name="y_pos"  select="$y_pos" />
                                            <xsl:with-param name="height" select="$y_height" />
                                        </xsl:call-template>

                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:copy-of select="." />
                                    </xsl:otherwise>
                                </xsl:choose>
                            </xsl:for-each>
                        </subject>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy-of select="." />
                    </xsl:otherwise>
                </xsl:choose>

            </xsl:for-each>
        </xsl:variable>

        <!-- When last tech in last subject has been processed, return value, otherwise recurse -->
        <xsl:choose>
            <xsl:when test="($current_tech_pos = $num_techs) and ($current_subject_pos = $num_subjects)">
                <xsl:copy-of select="$new_tech_subjects" />
            </xsl:when>
            <xsl:otherwise>
                <!-- calculate next technology/subject positions -->
                <xsl:choose>
                    <xsl:when test="$current_tech_pos &lt; $num_techs">
                        <!-- increment tech, not subject -->

                        <xsl:call-template name="give_tech_subject_positions">
                            <xsl:with-param name="tech_subjects"       select="$new_tech_subjects" />
                            <xsl:with-param name="current_tech_pos"    select="$current_tech_pos + 1"  />
                            <xsl:with-param name="current_subject_pos" select="$current_subject_pos"  />
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        <!-- increment subject, reset tech -->

                        <xsl:call-template name="give_tech_subject_positions">
                            <xsl:with-param name="tech_subjects"       select="$new_tech_subjects" />
                            <xsl:with-param name="current_tech_pos"    select="1"  />
                            <xsl:with-param name="current_subject_pos" select="$current_subject_pos + 1"  />
                        </xsl:call-template>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>


    <xsl:template name="give_tech_conform_positions">
        <xsl:param name="tech_conform" />
        <xsl:param name="current_pos">1</xsl:param>

        <!-- Recurse over each technology, giving each one, in turn, graphical values.
             Need to recurse, as we can only work out the position of a technology once
             all the previous ones have been calculated, due to dynamic height of each -->

        <xsl:variable name="new_tech_conform">
            <xsl:for-each select="$tech_conform/rdf:Description">
                <xsl:choose>
                    <!-- Only work out values for the current technology -->
                    <xsl:when test="position() = $current_pos">

                        <!-- 1. Get y position -->

                        <xsl:variable name="y_pos">
                            <xsl:choose>
                                <xsl:when test="position() = 1">0</xsl:when>
                                <xsl:otherwise>
                                    <!-- Get all previous heights -->
                                    <xsl:variable name="sum_prev_height" select="sum($tech_conform/rdf:Description[position() &lt; $current_pos]/height)" />

                                    <!-- y pos = all previous heights, plus all spacing between all previous, plus spacing for this one -->
                                    <xsl:value-of select="$sum_prev_height + ($default_tech_y_space * ($current_pos - 1))" />
                                </xsl:otherwise>
                            </xsl:choose>
                        </xsl:variable>

                        <!-- 2. Get height -->
                        <xsl:variable name="y_height">
                            <xsl:call-template name="get_tech_height" />
                        </xsl:variable>

                        <!-- 3. width and x pos will always be the same, so can now update the RDF description -->
                        <xsl:call-template name="update_rdf_with_graph_values">
                            <xsl:with-param name="x_pos"  select="$x_confoms_group" />
                            <xsl:with-param name="y_pos"  select="$y_pos" />
                            <xsl:with-param name="height" select="$y_height" />
                        </xsl:call-template>

                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy-of select="." />
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
        </xsl:variable>

        <!-- When last tech has been processed, return value, otherwise recurse -->
        <xsl:choose>
            <xsl:when test="$current_pos = count($tech_conform/rdf:Description)">
                <xsl:copy-of select="$new_tech_conform" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="give_tech_conform_positions">
                    <xsl:with-param name="tech_conform" select="$new_tech_conform" />
                    <xsl:with-param name="current_pos"  select="$current_pos + 1"  />
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>


    <xsl:template name="get_tech_height">
        <!-- The height of a given technology will the max value of the number
             of 'parts', or the number that it is 'part of' (minimum 1),
             multiplied by the default technology height (plus... that number
             miuns 1, multiplied by the spacing) -->

        <!-- Context, before calling this template, will be in the
             rdf:Description for the given tech -->

        <xsl:variable name="this_uri" select="@rdf:about" />

        <xsl:variable name="has_parts" select="count(dcterms:hasPart)" />
        <xsl:variable name="part_ofs"  select="count($rdf_copy/rdf:Description[dcterms:hasPart/@rdf:resource = $this_uri])" />

        <xsl:variable name="number_multiplier">
            <xsl:choose>
                <xsl:when test="($has_parts &lt; 1) and ($part_ofs &lt; 1)">1</xsl:when>
                <xsl:when test="$has_parts &gt; $part_ofs"><xsl:value-of select="$has_parts" /></xsl:when>
                <xsl:otherwise><xsl:value-of select="$part_ofs" /></xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

        <xsl:value-of select="($number_multiplier * $default_tech_height) +
                              (($number_multiplier - 1) * $default_tech_y_space)" />
    </xsl:template>

    <xsl:template name="update_rdf_with_graph_values">
        <xsl:param name="x_pos"  />
        <xsl:param name="y_pos"  />
        <xsl:param name="width"  select="$default_tech_width" />
        <xsl:param name="height" select="$default_tech_height" />

        <rdf:Description rdf:about="{@rdf:about}">
            <xsl:copy-of select="*" />

            <xpos><xsl:value-of select="$x_pos" /></xpos>
            <ypos><xsl:value-of select="$y_pos" /></ypos>
            <width><xsl:value-of select="$width" /></width>
            <height><xsl:value-of select="$height" /></height>
        </rdf:Description>
    </xsl:template>


    <xsl:template name="get_xml_subject_groups">

        <!-- Note - don't group technologies that a) are 'part of' another, or
             b) are 'conformed to' by other technologies  -->

        <xsl:variable name="subject_techs">
            <xsl:for-each select="rdf:Description">
                <xsl:variable name="this_uri" select="@rdf:about" />

                <xsl:if test="count($rdf_copy/rdf:Description[(dcterms:conformsTo/@rdf:resource = $this_uri) or
                                                              (dcterms:hasPart/@rdf:resource = $this_uri)]) = 0">
                    <xsl:copy-of select="." />
                </xsl:if>
            </xsl:for-each>
        </xsl:variable>

        <!-- Create list of 'unique' subjects -->
        <xsl:variable name="unique_subjects" select="$subject_techs/rdf:Description[not(dc:Subject = preceding-sibling::rdf:Description/dc:Subject)]/dc:Subject" />

        <!-- Loop over each subject, placing relevant technologies under each -->
        <xsl:for-each select="$unique_subjects">
            <xsl:variable name="subject" select="." />

            <subject name="{$subject}">

                <!-- Find matching technologies -->
                <xsl:for-each select="$subject_techs/rdf:Description[dc:Subject = $subject]">
                    <xsl:copy-of select="." />
                </xsl:for-each>
            </subject>
        </xsl:for-each>

    </xsl:template>


    <xsl:template name="get_xml_conform_groups">
        <xsl:for-each select="rdf:Description[@rdf:about = $rdf_copy/rdf:Description/dcterms:conformsTo/@rdf:resource]">
            <xsl:copy-of select="." />
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>