[SOLVED] XSLT 1.0 grouping for text output

Issue

For a project with soil data im trying to group by STRAT whenever the data changes with increasing depth.

Thanks to a lot of help already I am getting already onthat platform there are already some good results, but its not perfect still.

Example:

XML:

<LAYERS> 
    <LAYER DEPTHTO="1.00" PETRO="Sand" STRAT="geological_formation_1" />
    <LAYER DEPTHTO="94.00" PETRO="Sand" STRAT="geological_formation_1" />
    <LAYER DEPTHTO="94.20" INTV="2" INDEX_ZONE="-1" EGART="Lost_Data"/>
    <LAYER DEPTHTO="95.00" PETRO="Gravel" STRAT="geological_formation_1" />
    <LAYER DEPTHTO="100.00" PETRO="Sand" STRAT="geological_formation_2" />
    <LAYER DEPTHTO="100.50" PETRO="Mud" STRAT="geological_formation_4" />
    <LAYER DEPTHTO="101.50" PETRO="Sand" STRAT="geological_formation_4" />
    <LAYER DEPTHTO="101.80" PETRO="Mud" STRAT="geological_formation_5" />
    <LAYER DEPTHTO="102.90" PETRO="Mud" STRAT="geological_formation_3" />
    <LAYER DEPTHTO="103.00" PETRO="Sand" STRAT="geological_formation_3" />
    <LAYER DEPTHTO="103.25" INTV="2" INDEX_ZONE="-1" EGART="Lost_Data"/>
    <LAYER DEPTHTO="103.69" PETRO="Sand" STRAT="geological_formation_2"/>
    <LAYER DEPTHTO="104.00" PETRO="Mud" STRAT="geological_formation_2" />
</LAYERS> 

using with xslt:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>

<xsl:key name="layer-by-strat" match="LAYER" use="@STRAT" />

<xsl:template match="LAYERS" >
    <xsl:call-template name="generate-rows">
        <xsl:with-param name="layers" select="LAYER[not(@EGART='Lost_Data')][count(. | key('layer-by-strat', @STRAT)[1]) = 1]"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="generate-rows">
    <xsl:param name="layers" select="/.."/>
    <xsl:param name="accumulated-depth" select="'0.00'"/>
    <xsl:if test="$layers">
        <xsl:variable name="strat" select="$layers[1]/@STRAT" />
        <xsl:variable name="max-depth" select="key('layer-by-strat', $strat)[last()]/@DEPTHTO" />
        <!-- output -->
        <xsl:text>ZONE "</xsl:text>
        <xsl:value-of select="$strat" />
        <xsl:text>" </xsl:text>
        <xsl:value-of select="$accumulated-depth" />
        <xsl:text> </xsl:text>
        <xsl:value-of select="$max-depth" />
        <xsl:text>&#10;</xsl:text>
        <!-- recursive call -->
        <xsl:call-template name="generate-rows">
            <xsl:with-param name="layers" select="$layers[position() > 1]"/>
            <xsl:with-param name="accumulated-depth" select="$max-depth"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

the output is:

ZONE "geological_formation_1" 0.00 95.00
ZONE "geological_formation_2" 95.00 104.00
ZONE "geological_formation_4" 104.00 101.50
ZONE "geological_formation_5" 101.50 101.80
ZONE "geological_formation_3" 101.80 103.00

BUT i need an output like that:

ZONE "geologiscal_formation_1" 0.00 95.00
ZONE "geologiscal_formation_2" 95.00 100.00
ZONE "geologiscal_formation_4" 100.00 101.50
ZONE "geologiscal_formation_5" 101.50 101.80
ZONE "geologiscal_formation_3" 101.80 103.00
ZONE "geologiscal_formation_2" 103.25 104.00

The tricky parts are that "Lost_Data" is common and need to be ignored in the grouping process.

If there are any ideas how to accomplish/improve the stylesheet or maybe using a new way to get the desired output, id be very thankful.

Kind regards

UPDATE:
I was tinkering around and got the the point where it almost works, but i cannot ignore LAYER with EGART="Lost_Data". ALso the first group needs a 0.00 as the first number

XSLT:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>

<xsl:key name="adj" match="LAYER" use="generate-id(preceding-sibling::LAYER[not(@STRAT = current()/@STRAT)][1])" />

    <xsl:template name="GOCAD" match="/*">
        <xsl:apply-templates select="." mode="ZONES"/>
    </xsl:template>


  <xsl:template name="ZONES" match="/*" mode="ZONES">
   <xsl:for-each select="LAYER[generate-id() = generate-id(key('adj', generate-id(preceding-sibling::LAYER[not(@STRAT = current()/@STRAT)][1]))[1])]">
      <xsl:variable name="current-group" select="key('adj', generate-id(preceding-sibling::LAYER[not(@STRAT = current()/@STRAT)][1]))" />
      <xsl:variable name="precedingZONE" select="preceding-sibling::LAYER[1]"/>
      <xsl:variable name="DEPTHFROM" select="$precedingZONE/@DEPTHTO"/>
      
      <xsl:text>ZONE "</xsl:text>
      <xsl:value-of select="@STRAT"/>
      <xsl:text>" </xsl:text>
      <xsl:for-each select="$current-group">
        <xsl:sort select="$DEPTHFROM" data-type="number" order="ascending"/>
        <xsl:if test="position() = 1 ">
          <xsl:value-of select="$DEPTHFROM"/>
        </xsl:if>
      </xsl:for-each>
      <xsl:text> </xsl:text>
      <xsl:for-each select="$current-group">
        <xsl:sort select="@DEPTHTO" data-type="number" order="descending"/>
        <xsl:if test="position() = 1 ">
          <xsl:value-of select="@DEPTHTO"/>
        </xsl:if>
      </xsl:for-each>
      <xsl:text> -1</xsl:text>
      <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

Output:

ZONE "geological_formation_1"  94.00 -1
ZONE "" 94.00 94.20 -1
ZONE "geological_formation_1" 94.20 95.00 -1
ZONE "geological_formation_2" 95.00 100.00 -1
ZONE "geological_formation_4" 100.00 101.50 -1
ZONE "geological_formation_5" 101.50 101.80 -1
ZONE "geological_formation_3" 101.80 103.00 -1
ZONE "" 103.00 103.25 -1
ZONE "geological_formation_2" 103.25 104.00 -1

Solution

This should get you going. It doesn’t account for EGART="Lost_Data" being in the first position. But, you should be able to make that adjustment if you need to.

<xsl:stylesheet version="1.0"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" version="1.0" indent="yes" />

  <xsl:variable name="allLayers">
    <xsl:for-each select="//LAYER">
      <xsl:variable name="STRAT" select="@STRAT"/>
      
      <xsl:element name="element">
        <xsl:element name="first">
          <xsl:if test="boolean(@STRAT) and not(preceding-sibling::LAYER[boolean(@STRAT)][1]/@STRAT = $STRAT)">
            <xsl:value-of select="'true'"/>
          </xsl:if>
        </xsl:element>
        <xsl:element name="STRAT">
          <xsl:value-of select="@STRAT"/>
        </xsl:element>
        <xsl:element name="id">
          <xsl:value-of select="generate-id(.)"/>
        </xsl:element>      
      </xsl:element>
    </xsl:for-each>
  </xsl:variable>

  <!-- msxml is a namespace.  You will have to use you own namespace. -->
  <xsl:variable name="allLayersList" select="msxml:node-set($allLayers)"/>

  <xsl:variable name="layers">
    <xsl:for-each select="//LAYER">
      <xsl:variable name="id" select="generate-id(.)"/>
    
      <xsl:variable name="STRAT">
        <xsl:choose>
          <xsl:when test="boolean(@STRAT)">
            <xsl:value-of select="@STRAT"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="preceding-sibling::LAYER[boolean(@STRAT)][1]/@STRAT"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
    
      <xsl:element name="element">
        <xsl:element name="id">
          <xsl:value-of select="generate-id(.)"/>
        </xsl:element>
        <xsl:element name="group">
          <xsl:choose>
            <xsl:when test="$allLayersList/element[id = $id and first = 'true']">
              <xsl:value-of select="$id"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="$allLayersList/element[id = $id]/preceding-sibling::element[first = 'true'][1]/id"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:element>
          <xsl:element name="STRAT">
            <xsl:value-of select="$STRAT"/>
          </xsl:element>
        <xsl:element name="DEPTHTO">
          <xsl:value-of select="@DEPTHTO"/>
        </xsl:element>
      </xsl:element>
    </xsl:for-each>
  </xsl:variable>

  <!-- msxml is a namespace.  You will have to use you own namespace. -->
  <xsl:variable name="layerList" select="msxml:node-set($layers)"/>

  <xsl:template match="LAYER">
    <xsl:variable name="id" select="generate-id(.)"/>
    <xsl:variable name="group" select="$layerList/element[id = $id]/group"/>
    <xsl:variable name="strat" select="$layerList/element[id = $id]/STRAT"/>
    <!-- Is this the first in the group.  -->
    <xsl:if test="$allLayersList/element[id = $id and first = 'true']">
     <xsl:variable name="accumulated-depth">
        <xsl:choose>
          <!-- Is there a preceding group -->
          <xsl:when test="$layerList/element[group = $group][1]/preceding-sibling::element[1]">
            <xsl:variable name="precedingGroup" select="$layerList/element[group = $group][1]/preceding-sibling::element[1]/group"/>
            <xsl:for-each select="$layerList/element[group = $precedingGroup]">
              <xsl:sort select="DEPTHTO" data-type="number" order="descending"/>
              <xsl:if test="position() = 1">
                <xsl:value-of select="DEPTHTO"/>
              </xsl:if>
            </xsl:for-each>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="'0.00'"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
    
      <xsl:variable name="max-depth">
        <xsl:for-each select="$layerList/element[group = $group]">
          <xsl:sort select="DEPTHTO" data-type="number" order="descending"/>
          <xsl:if test="position() = 1">
            <xsl:value-of select="DEPTHTO"/>
          </xsl:if>
        </xsl:for-each>
      </xsl:variable>
    
      <xsl:text>ZONE "</xsl:text>
      <xsl:value-of select="$strat" />
      <xsl:text>" </xsl:text>
      <xsl:value-of select="$accumulated-depth" />
      <xsl:text> </xsl:text>
      <xsl:value-of select="$max-depth" />
      <xsl:text>&#10;</xsl:text>
    </xsl:if>
  </xsl:template>

  <xsl:template match="node()|@*">
    <xsl:apply-templates select="node()|@*"/>
  </xsl:template>
</xsl:stylesheet>

Answered By – John Ernst

Answer Checked By – Jay B. (BugsFixing Admin)

Leave a Reply

Your email address will not be published.