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> </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> </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
CodePudding user response:
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> </xsl:text>
</xsl:if>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:apply-templates select="node()|@*"/>
</xsl:template>
</xsl:stylesheet>