这是我的实际数据(gps跟踪点)的简化版本。 我有一个按升序排列的时间列表。
<gpx>
<trk>
<trkseg>
<trkpt>
<time>2000-01-01T15:25:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T15:26:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T15:27:00Z</time>
</trkpt>
</trkseg>
</trk>
<trk>
<trkseg>
<trkpt>
<time>2000-01-01T15:28:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T15:29:00Z</time>
</trkpt>
</trkseg>
<trkseg>
<trkpt>
<time>2000-01-01T16:00:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T16:01:00Z</time>
</trkpt>
</trkseg>
</trk>
</gpx>
我想检测&#34;停止&#34;并将连续的时间分组。 例如,使用&#34;停止&#34;如果没有5分钟或更长时间的数据,我会得到
<gpx >
<trk>
<trkseg>
<trkpt>
<time>2000-01-01T15:25:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T15:26:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T15:27:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T15:28:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T15:29:00Z</time>
</trkpt>
</trkseg>
</trk>
<trk>
<trkseg>
<trkpt>
<time>2000-01-01T16:00:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T16:01:00Z</time>
</trkpt>
</trkseg>
</trk>
</gpx>
答案 0 :(得分:1)
这是XQuery 3.0 / 3.1中tumbling window子句的一个很好的用例:
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare option output:indent "yes";
<gpx>
{
let $dateTimes := //time/xs:dateTime(.),
$stopDuration := xs:dayTimeDuration('PT5M')
for tumbling window $w in $dateTimes
start when true()
end $e next $n when $n - $e gt $stopDuration
return
<trk>
<trkseg>
{
$w!<trkpt><time>{.}</time></trkpt>
}
</trkseg>
</trk>
}
</gpx>
结果是
<?xml version="1.0" encoding="UTF-8"?>
<gpx>
<trk>
<trkseg>
<trkpt>
<time>2000-01-01T15:25:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T15:26:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T15:27:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T15:28:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T15:29:00Z</time>
</trkpt>
</trkseg>
</trk>
<trk>
<trkseg>
<trkpt>
<time>2000-01-01T16:00:00Z</time>
</trkpt>
<trkpt>
<time>2000-01-01T16:01:00Z</time>
</trkpt>
</trkseg>
</trk>
</gpx>
因此,如果您的XSLT处理器是Saxon 9,它也支持XQuery,您可以考虑使用XQuery而不是XSLT。
如果你想在XSLT中使用它,那么这里是一个XSLT 3.0样式表,可以与Saxon 9.8(任何版本)或Altova(2017版本)一起运行,试图使用xsl:iterate
来模拟XQuery方法上面显示的翻滚窗口计算:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs math mf"
expand-text="yes"
version="3.0">
<xsl:variable name="stop" as="xs:dayTimeDuration" select="xs:dayTimeDuration('PT5M')"/>
<xsl:output indent="yes"/>
<xsl:function name="mf:wrap" as="element(trk)">
<xsl:param name="dateTimes" as="xs:dateTime*"/>
<trk>
<trkseq>
<xsl:for-each select="$dateTimes">
<trkpt>
<time>
<xsl:value-of select="."/>
</time>
</trkpt>
</xsl:for-each>
</trkseq>
</trk>
</xsl:function>
<xsl:template match="gpx">
<xsl:copy>
<xsl:variable name="dateTimes" as="xs:dateTime*" select=".//time/xs:dateTime(.)"/>
<xsl:iterate select="$dateTimes">
<xsl:param name="w" as="xs:dateTime*" select="()"/>
<xsl:param name="e" as="xs:dateTime?" select="()"/>
<xsl:on-completion>
<xsl:sequence select="mf:wrap($w)"/>
</xsl:on-completion>
<xsl:variable name="n" as="xs:dateTime" select="."/>
<xsl:choose>
<xsl:when test="exists($e) and exists($n) and $n - $e gt $stop">
<xsl:sequence select="mf:wrap($w)"/>
<xsl:next-iteration>
<xsl:with-param name="w" select="$n"/>
<xsl:with-param name="e" select="$n"/>
</xsl:next-iteration>
</xsl:when>
<xsl:otherwise>
<xsl:next-iteration>
<xsl:with-param name="w" select="$w, $n"/>
<xsl:with-param name="e" select="$n"/>
</xsl:next-iteration>
</xsl:otherwise>
</xsl:choose>
</xsl:iterate>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
如果您需要在XSLT 2.0中执行此操作,一种方法是编写一个逐项递归处理dateTime序列的函数,并在找到与日期相比具有更大差异的日期时,分别返回一个组trk
元素。允许限制(所以基本上实现了XQuery检查end $e next $n when $n - $e gt $stopDuration
):
<?xml version="1.0" encoding="UTF-8"?>
<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">
<xsl:param name="stop" as="xs:dayTimeDuration" select="xs:dayTimeDuration('PT5M')"/>
<xsl:output indent="yes"/>
<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>
<trkseq>
<xsl:for-each select="$dateTimes">
<trkpt>
<time>
<xsl:value-of select="."/>
</time>
</trkpt>
</xsl:for-each>
</trkseq>
</trk>
</xsl:function>
<xsl:template match="gpx">
<xsl:copy>
<xsl:sequence select="mf:group(.//time/xs:dateTime(.), $stop)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>