Search code examples
xqueryxslt-2.0

How to keep a pointer to the original data in a tumbling window


Following my inital question with simplified data, I try to adapt the given solution to my actual data.

I face now this problem: what is the best way to group the <time> nodes (as anwsered) and keep a pointer to the source document. Preciselly, I need to access the parent node <trkpt> to copy the attributes in the output.

Input

<?xml version="1.0" encoding="UTF-8"?>
<gpx 
xmlns="http://www.topografix.com/GPX/1/1" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
creator="me" 
version="1.1" 
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 gpx.xsd">
    <metadata>
        <link href="http://www.garmin.com">
            <text>Garmin International</text>
        </link>
        <time>2017-08-03T11:26:14Z</time>
    </metadata>
    <trk>
        <name>Journal actif: 01 AOUT 2017 16:03</name>
        <trkseg>
            <trkpt lat="50.064145" lon="5.194660">
                <ele>305.84</ele>
                <time>2017-08-01T15:25:58Z</time>
            </trkpt>
            <trkpt lat="50.062084" lon="5.198431">
                <ele>314.49</ele>
                <time>2017-08-01T15:26:11Z</time>
            </trkpt>
            <trkpt lat="50.059504" lon="5.202687">
                <ele>321.70</ele>
                <time>2017-08-01T15:26:27Z</time>
            </trkpt>
        </trkseg>
    </trk>
    <trk>
        <name>Journal actif: 01 AOUT 2017 17:26</name>
        <trkseg>
            <trkpt lat="50.058567" lon="5.203909">
                <ele>323.62</ele>
                <time>2017-08-01T15:26:32Z</time>
            </trkpt>
            <trkpt lat="50.055699" lon="5.207007">
                <ele>330.35</ele>
                <time>2017-08-01T15:26:46Z</time>
            </trkpt>            
        </trkseg>
    </trk>
</gpx>

Expected Output (partial view)

<trkseg>
    <trkpt lat="50.064145" lon="5.194660">
        <time>2017-08-03T11:26:14Z</time>
    </trkpt>

My current xslt is as follow It breaks at the line <xsl:attribute name="lat" select="../@lat"/>.

<?xml version="1.0" encoding="UTF-8"?>
<?altova_samplexml file:///C:/Data/Google%20Drive/Projects%20-%20Coding/Xslt/Garmin/01.xml?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mf="http://example.com/mf" exclude-result-prefixes="xs mf" version="2.0" xpath-default-namespace="http://www.topografix.com/GPX/1/1">
    <xsl:param name="stop" as="xs:dayTimeDuration" select="xs:dayTimeDuration('PT5M')"/>
    <xsl:output indent="yes" method="xml" />
    <xsl:function name="mf:group" as="element(trk)*">
        <xsl:param name="dateTimes" as="xs:dateTime*"/>
        <xsl:param name="stop" as="xs:dayTimeDuration"/>
        <xsl:sequence select="mf:group($dateTimes[1], $dateTimes[position() gt 1], $stop)"/>
    </xsl:function>
    <xsl:function name="mf:group" as="element(trk)*">
        <xsl:param name="group" as="xs:dateTime*"/>
        <xsl:param name="dateTimes" as="xs:dateTime*"/>
        <xsl:param name="stop" as="xs:dayTimeDuration"/>
        <xsl:variable name="next" as="xs:dateTime?" select="$dateTimes[1]"/>
        <xsl:variable name="end" as="xs:dateTime" select="$group[last()]"/>
        <xsl:choose>
            <xsl:when test="not(exists($next))">
                <xsl:sequence select="mf:wrap($group)"/>
            </xsl:when>
            <xsl:when test="$next - $end gt $stop">
                <xsl:sequence select="mf:wrap($group)"/>
                <xsl:sequence select="mf:group($next, $dateTimes[position() gt 1], $stop)"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence select="mf:group(($group, $next), $dateTimes[position() gt 1], $stop)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>
    <xsl:function name="mf:wrap" as="element(trk)">
        <xsl:param name="dateTimes" as="xs:dateTime*"/>
        <trk xmlns="http://www.topografix.com/GPX/1/1">
            <name>TBC</name>
            <trkseg>
                <xsl:for-each select="$dateTimes">
                    <trkpt>
                    <xsl:attribute name="lat" select="../@lat"/>
                    <xsl:attribute name="lon" select="../@lon"/>
                        <time>
                            <xsl:value-of select="."/>
                        </time>
                    </trkpt>
                </xsl:for-each>
            </trkseg>
        </trk>
    </xsl:function>
    <xsl:template match="gpx">
        <gpx 
        xmlns="http://www.topografix.com/GPX/1/1" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        creator="me" 
        version="1.1" 
        xsi:schemaLocation="http://www.topografix.com/GPX/1/1 gpx.xsd">
            <xsl:sequence select="mf:group(.//trkpt/time/xs:dateTime(.), $stop)"/>
        </gpx>
    </xsl:template>
</xsl:stylesheet>

Solution

  • In that case you would need to rewrite the functions to pass on and handle the elements (like the trkpt elements) and then inside the functions you would need to compute the xs:dateTime for the comparison), here is an attempt to fix the functions, I haven't tested that thouroughly though:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mf="http://example.com/mf" exclude-result-prefixes="xs mf" version="2.0" xpath-default-namespace="http://www.topografix.com/GPX/1/1">
        <xsl:param name="stop" as="xs:dayTimeDuration" select="xs:dayTimeDuration('PT5M')"/>
        <xsl:output indent="yes" method="xml" />
        <xsl:strip-space elements="*"/>
        <xsl:function name="mf:group" as="element(trk)*">
            <xsl:param name="trktpts" as="element(trkpt)*"/>
            <xsl:param name="stop" as="xs:dayTimeDuration"/>
            <xsl:sequence select="mf:group($trktpts[1], $trktpts[position() gt 1], $stop)"/>
        </xsl:function>
        <xsl:function name="mf:group" as="element(trk)*">
            <xsl:param name="group" as="element(trkpt)*"/>
            <xsl:param name="trkpts" as="element(trkpt)*"/>
            <xsl:param name="stop" as="xs:dayTimeDuration"/>
            <xsl:variable name="next" as="element(trkpt)?" select="$trkpts[1]"/>
            <xsl:variable name="nextDateTime" as="xs:dateTime?" select="$next/time/xs:dateTime(.)"/>
            <xsl:variable name="end" as="xs:dateTime" select="$group[last()]/time/xs:dateTime(.)"/>
            <xsl:choose>
                <xsl:when test="not(exists($next))">
                    <xsl:sequence select="mf:wrap($group)"/>
                </xsl:when>
                <xsl:when test="$nextDateTime - $end gt $stop">
                    <xsl:sequence select="mf:wrap($group)"/>
                    <xsl:sequence select="mf:group($next, $trkpts[position() gt 1], $stop)"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:sequence select="mf:group(($group, $next), $trkpts[position() gt 1], $stop)"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:function>
        <xsl:function name="mf:wrap" as="element(trk)">
            <xsl:param name="trkpts" as="element(trkpt)*"/>
            <trk>
                <name>TBC</name>
                <trkseg>
                    <xsl:apply-templates select="$trkpts"/>
                </trkseg>
            </trk>
        </xsl:function>
    
        <xsl:template match="@* | node()">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="trkpt/ele"/>
    
        <xsl:template match="gpx">
            <gpx 
                creator="me" 
                version="1.1" 
                xsi:schemaLocation="http://www.topografix.com/GPX/1/1 gpx.xsd">
                <xsl:sequence select="mf:group(.//trkpt, $stop)"/>
            </gpx>
        </xsl:template>
    </xsl:stylesheet>