xsl-list
[Top] [All Lists]

RE: [xsl] Calculating cumulative values - Abel's solution

2007-08-31 09:56:21
Abel,

The example input I provided in my original question was simplified so that
I could convey the problem more clearly and so that I would have a better
chance of understanding the solutions offered.  In retrospect, I can see why
you guys always ask for a 'real' data set when helping us newbies out :).
Anyhow, with that off my chest I was able to adapt your stylesheet to the
'real' data (SVG).

The question I have now is how to replicate the top three levels in the
hierarchy to the output.  Do I have to change the pattern match on the root
node?

Thanks,

Simon


Here's the 'real' data:

<g id="theroot" xmlns:myns="http://www.acme.com/myns";>
  <g id="groupA">
    <svg x="10" y="10" width="1000" height="1000" viewBox="0 0 1000 1000">
      <g transform="scale(1,1)">
        <rect x="1" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="1" myns:y1="3" width="100" height="100" fill="red" />
        <rect x="1" myns:y1="0" width="100" height="100" fill="red" />
        <rect x="1" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="1" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="2" myns:y1="3" width="100" height="100" fill="red" />
        <rect x="2" myns:y1="0" width="100" height="100" fill="red" />
        <rect x="2" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="3" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="3" myns:y1="3" width="100" height="100" fill="red" />
        <rect x="3" myns:y1="1" width="100" height="100" fill="red" />
        <rect x="3" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="3" myns:y1="2" width="100" height="100" fill="red" />
      </g>
    </svg>
  </g>
  <g id="groupB">
    <svg x="10" y="1010" width="1000" height="1000" viewBox="0 0 1000 1000">
      <g transform="scale(1,1)">
        <rect x="1" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="1" myns:y1="3" width="100" height="100" fill="red" />
        <rect x="1" myns:y1="0" width="100" height="100" fill="red" />
        <rect x="1" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="2" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="3" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="3" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="3" myns:y1="2" width="100" height="100" fill="red" />
      </g>
    </svg>
  </g>
  <g id="groupC">
    <svg x="10" y="2010" width="1000" height="1000" viewBox="0 0 1000 1000">
      <g transform="scale(1,1)">
        <rect x="1" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="1" myns:y1="3" width="100" height="100" fill="red" />
        <rect x="1" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="2" myns:y1="3" width="100" height="100" fill="red" />
        <rect x="2" myns:y1="0" width="100" height="100" fill="red" />
        <rect x="2" myns:y1="2" width="100" height="100" fill="red" />
        <rect x="3" myns:y1="3" width="100" height="100" fill="red" />
      </g>
    </svg>
  </g>
</g>
Here's how I adapted your solution:


<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
    xmlns:xs="http://www.w3.org/2001/XMLSchema";
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="2.0"
    xmlns:myns="http://www.acme.com/myns";>
  <xsl:output indent="yes" />

  <xsl:template match="/">
    <!-- mini pipeline: put rects into a variable and process them -->
    <xsl:variable name="rects">
      <xsl:apply-templates select="g/g[1]/svg[1]/g[1]/rect[1]"
mode="aggregate">
        <xsl:with-param name="calc" tunnel="yes">
          <for x="1" y2="2" y3="2"/>
        </xsl:with-param>
      </xsl:apply-templates>
    </xsl:variable>

    <!-- apply g with pre-processed rects -->
    <xsl:apply-templates select="g/g/svg/g">
      <xsl:with-param name="rects" select="$rects" />
    </xsl:apply-templates>
  </xsl:template>

  <!-- get calculated values -->
  <xsl:template match="rect" use-when="1">
    <xsl:copy>
      <xsl:copy-of select="@x | @myns:y1 | @y2 | @y3 | @width | @height |
@fill" />
    </xsl:copy>
  </xsl:template>

  <!-- apply the calculated rects belonging to the current g -->
  <xsl:template match="g/g/svg/g">
    <xsl:param name="rects" />

    <xsl:copy>
      <xsl:copy-of select="@*" />
      <xsl:apply-templates select="$rects/rect[(_at_)parent-id =
generate-id(current())]" />
    </xsl:copy>
  </xsl:template>

  <!-- calculation of the rects (needs some cleaning) -->
  <xsl:template match="rect" mode="aggregate">
    <xsl:param name="calc" tunnel="yes" />

    <!-- helper variables -->
    <xsl:variable name="next-rect" select="following::rect[1]" />
    <xsl:variable name="next-x" select="$next-rect/@x" />
    <xsl:variable name="next-y" select="($next-rect/@myns:y1, 0)[1]"
as="xs:integer" />

    <!--
             the $calc block contains the current calculation,
             index is based on the @x value (i.e., one calc block per @x
value
         -->
    <xsl:variable name="current-calc" select="$calc/for[(_at_)x = current()/@x]"
/>
    <xsl:variable name="next-calc" select="$calc/for[(_at_)x = $next-x]" />
    <xsl:variable name="next-y3" select="xs:integer(($next-calc/@y3,
0)[1])" as="xs:integer" />

    <!-- holds 1 in normal situation, 2 when a new x value arrives -->
    <xsl:variable name="new-x-value" select="number(@x = $next-x) + 1" />

    <xsl:copy>
      <xsl:copy-of select="@*" />
      <xsl:attribute name="y2" select="$current-calc/@y2" />
      <xsl:attribute name="y3" select="$current-calc/@y3" />
      <xsl:attribute name="parent-id"
select="generate-id(parent::node())" />

      <!-- copy is for debugging purposes, remove to prevent bloating -->
      <xsl:copy-of select="$calc" />
    </xsl:copy>

    <xsl:variable name="var">

    </xsl:variable>

    <xsl:apply-templates select="following::rect[1]" mode="#current" >
      <xsl:with-param name="calc" tunnel="yes">

        <xsl:variable name="new-for">
          <xsl:variable
              name="source-y2"
              select="(0, $next-rect/@myns:y1)[$new-x-value]"
              as="xs:integer" />
          <xsl:variable
              name="prev-y2"
              select="($next-rect/@myns:y1,
$current-calc/@y2)[$new-x-value]"
              as="xs:integer" />

          <xsl:variable name="sum-y3" select="$next-y3 + $next-y" />

          <for
              x="{$next-rect/@x}"
              y2="{$source-y2 + $prev-y2}"
              y3="{$sum-y3}"  />

        </xsl:variable>

        <!-- select the new one only for the current x -->
        <xsl:for-each select="distinct-values(($calc/for/@x, $next-x))">
          <xsl:copy-of
              select="
                            ($new-for/for[(_at_)x = current()],
                            $calc/for[(_at_)x = current()])[1]" />
        </xsl:for-each>

      </xsl:with-param>
    </xsl:apply-templates>
  </xsl:template>
</xsl:stylesheet>

And here's what I am outputting:

<?xml version="1.0" encoding="UTF-8"?>
<g xmlns:myns="http://www.acme.com/myns"; transform="scale(1,1)">
   <rect x="1" myns:y1="2" width="100" height="100" fill="red" y2="2"
y3="2"/>
   <rect x="1" myns:y1="3" width="100" height="100" fill="red" y2="5"
y3="5"/>
   <rect x="1" myns:y1="0" width="100" height="100" fill="red" y2="5"
y3="5"/>
   <rect x="1" myns:y1="2" width="100" height="100" fill="red" y2="7"
y3="7"/>
   <rect x="1" myns:y1="2" width="100" height="100" fill="red" y2="9"
y3="9"/>
   <rect x="2" myns:y1="3" width="100" height="100" fill="red" y2="3"
y3="3"/>
   <rect x="2" myns:y1="0" width="100" height="100" fill="red" y2="3"
y3="3"/>
   <rect x="2" myns:y1="2" width="100" height="100" fill="red" y2="5"
y3="5"/>
   <rect x="3" myns:y1="2" width="100" height="100" fill="red" y2="2"
y3="2"/>
   <rect x="3" myns:y1="3" width="100" height="100" fill="red" y2="5"
y3="5"/>
   <rect x="3" myns:y1="1" width="100" height="100" fill="red" y2="6"
y3="6"/>
   <rect x="3" myns:y1="2" width="100" height="100" fill="red" y2="8"
y3="8"/>
   <rect x="3" myns:y1="2" width="100" height="100" fill="red" y2="10"
y3="10"/>
</g>
<g xmlns:myns="http://www.acme.com/myns"; transform="scale(1,1)">
   <rect x="1" myns:y1="2" width="100" height="100" fill="red" y2="2"
y3="11"/>
   <rect x="1" myns:y1="3" width="100" height="100" fill="red" y2="5"
y3="14"/>
   <rect x="1" myns:y1="0" width="100" height="100" fill="red" y2="5"
y3="14"/>
   <rect x="1" myns:y1="2" width="100" height="100" fill="red" y2="7"
y3="16"/>
   <rect x="2" myns:y1="2" width="100" height="100" fill="red" y2="2"
y3="7"/>
   <rect x="3" myns:y1="2" width="100" height="100" fill="red" y2="2"
y3="12"/>
   <rect x="3" myns:y1="2" width="100" height="100" fill="red" y2="4"
y3="14"/>
   <rect x="3" myns:y1="2" width="100" height="100" fill="red" y2="6"
y3="16"/>
</g>
<g xmlns:myns="http://www.acme.com/myns"; transform="scale(1,1)">
   <rect x="1" myns:y1="2" width="100" height="100" fill="red" y2="2"
y3="18"/>
   <rect x="1" myns:y1="3" width="100" height="100" fill="red" y2="5"
y3="21"/>
   <rect x="1" myns:y1="2" width="100" height="100" fill="red" y2="7"
y3="23"/>
   <rect x="2" myns:y1="3" width="100" height="100" fill="red" y2="3"
y3="10"/>
   <rect x="2" myns:y1="0" width="100" height="100" fill="red" y2="3"
y3="10"/>
   <rect x="2" myns:y1="2" width="100" height="100" fill="red" y2="5"
y3="12"/>
   <rect x="3" myns:y1="3" width="100" height="100" fill="red" y2="3"
y3="19"/>
</g>


--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--

<Prev in Thread] Current Thread [Next in Thread>