--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- eccc_merger.xslt - Merge XML historical data as provided by
+ Environment and Climate Change Canada
+ Copyright (C) 2020 Pierre Choffet
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+ Rounding is always done one digit after decimal point.
+
+ Output data is calculated as such:
+ temperature.extremeMax : Greatest of all values
+ temperature.extremeMin : Lowest of all values
+ temperature.normalMax : Greatest of all values
+ temperature.normalMin : Lowest of all values
+ temperature.normalMean : Weighted mean of all values
+ precipitation.extremeRainfall : Greatest of all values
+ precipitation.extremeSnowfall : Greatest of all values
+ precipitation.extremePrecipitation: Greatest of all values
+ precipitation.extremeSnowOnGround : Greatest of all values
+ pop : Weighted mean of all values
+ -->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ exclude-result-prefixes="xsi">
+
+ <xsl:output method="xml" encoding="utf-8" />
+
+ <xsl:variable name="input-root" select="/" />
+ <xsl:variable name="merge-doc" select="document($merge-path)" />
+
+ <!-- Idempotence -->
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="/climatedata">
+ <xsl:copy>
+ <!-- Upon first merge, import missing metadata elements -->
+ <xsl:if test="not(lang)">
+ <xsl:apply-templates select="$merge-doc/climatedata/lang" />
+ </xsl:if>
+ <xsl:if test="not(stationinformation)">
+ <xsl:apply-templates select="$merge-doc/climatedata/stationinformation" />
+ </xsl:if>
+
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="month">
+ <xsl:copy>
+ <xsl:apply-templates select="@index" />
+
+ <!-- ECCC data may not contain all days element, so we're counting by ourserves -->
+ <xsl:call-template name="loop-day">
+ <xsl:with-param name="day" select="1" />
+ </xsl:call-template>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="day">
+ <xsl:copy>
+ <xsl:attribute name="index">
+ <xsl:value-of select="@index" />
+ </xsl:attribute>
+
+ <xsl:apply-templates match="node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="temperature[@class = 'extremeMax']">
+ <xsl:variable name="month" select="../../@index" />
+ <xsl:variable name="day" select="../@index" />
+ <xsl:variable name="input-extreme-max" select="$input-root/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'extremeMax']" />
+ <xsl:variable name="merge-extreme-max" select="$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'extremeMax']" />
+
+ <xsl:choose>
+ <xsl:when test="$input-extreme-max and (not($merge-extreme-max) or $input-extreme-max/text() >= $merge-extreme-max/text())">
+ <xsl:copy>
+ <xsl:apply-templates select="$input-extreme-max/@*" />
+ <xsl:apply-templates select="$input-extreme-max/node()" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy>
+ <xsl:apply-templates select="$merge-extreme-max/@*" />
+ <xsl:apply-templates select="$merge-extreme-max/node()" />
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="temperature[@class = 'extremeMin']">
+ <xsl:variable name="month" select="../../@index" />
+ <xsl:variable name="day" select="../@index" />
+ <xsl:variable name="input-extreme-min" select="$input-root/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'extremeMin']" />
+ <xsl:variable name="merge-extreme-min" select="$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'extremeMin']" />
+
+ <xsl:choose>
+ <xsl:when test="$input-extreme-min and (not($merge-extreme-min) or $input-extreme-min/text() <= merge-extreme-min/text())">
+ <xsl:copy>
+ <xsl:apply-templates select="$input-extreme-min/@*" />
+ <xsl:apply-templates select="$input-extreme-min/node()" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy>
+ <xsl:apply-templates select="$merge-extreme-min/@*" />
+ <xsl:apply-templates select="$merge-extreme-min/node()" />
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="temperature[@class = 'normalMax']">
+ <xsl:variable name="month" select="../../@index" />
+ <xsl:variable name="day" select="../@index" />
+ <xsl:variable name="input-normal-max" select="$input-root/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'normalMax']" />
+ <xsl:variable name="merge-normal-max" select="$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'normalMax']" />
+
+ <xsl:choose>
+ <xsl:when test="$input-normal-max and (not($merge-normal-max) or $input-normal-max/text() >= $merge-normal-max/text())">
+ <xsl:copy>
+ <xsl:apply-templates select="$input-normal-max/@*" />
+ <xsl:apply-templates select="$input-normal-max/node()" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy>
+ <xsl:apply-templates select="$merge-normal-max/@*" />
+ <xsl:apply-templates select="$merge-normal-max/node()" />
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="temperature[@class = 'normalMin']">
+ <xsl:variable name="month" select="../../@index" />
+ <xsl:variable name="day" select="../@index" />
+ <xsl:variable name="input-normal-min" select="$input-root/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'normalMin']" />
+ <xsl:variable name="merge-normal-min" select="$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'normalMin']" />
+
+ <xsl:choose>
+ <xsl:when test="$input-normal-min and (not($merge-normal-min) or $input-normal-min/text() <= $merge-normal-min/text())">
+ <xsl:copy>
+ <xsl:apply-templates select="$input-normal-min/@*" />
+ <xsl:apply-templates select="$input-normal-min/node()" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy>
+ <xsl:apply-templates select="$merge-normal-min/@*" />
+ <xsl:apply-templates select="$merge-normal-min/node()" />
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="temperature[@class = 'normalMean']">
+ <xsl:variable name="month" select="../../@index" />
+ <xsl:variable name="day" select="../@index" />
+ <xsl:variable name="input-normal-mean" select="$input-root/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'normalMean']" />
+ <xsl:variable name="merge-normal-mean" select="$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'normalMean']" />
+
+ <xsl:choose>
+ <xsl:when test="$input-normal-mean and $input-normal-mean/text() != '' and $merge-normal-mean and $merge-normal-mean/text() != ''">
+ <xsl:copy>
+ <xsl:apply-templates select="$input-normal-mean/@*" />
+ <xsl:attribute name="values-count"><xsl:value-of select="$input-normal-mean/@values-count + 1" /></xsl:attribute>
+ <xsl:value-of select="format-number(number($input-normal-mean/text() * $input-normal-mean/@values-count + $merge-normal-mean/text()) div number($input-normal-mean/@values-count + 1), '0.0')" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:when test="$input-normal-mean and (not($merge-normal-mean) or $merge-normal-mean/text() = '')">
+ <xsl:copy>
+ <xsl:apply-templates select="$input-normal-mean/@*" />
+ <xsl:apply-templates select="$input-normal-mean/node()" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:when test="not($input-normal-mean) and $merge-normal-mean and $merge-normal-mean/text() != ''">
+ <xsl:copy>
+ <xsl:apply-templates select="$merge-normal-mean/@*" />
+ <xsl:attribute name="values-count">1</xsl:attribute>
+ <xsl:apply-templates select="$merge-normal-mean/node()" />
+ </xsl:copy>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="precipitation[@class = 'extremeRainfall']">
+ <xsl:variable name="month" select="../../@index" />
+ <xsl:variable name="day" select="../@index" />
+ <xsl:variable name="input-precipitation-rainfall" select="$input-root/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremeRainfall']" />
+ <xsl:variable name="merge-precipitation-rainfall" select="$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremeRainfall']" />
+
+ <xsl:choose>
+ <xsl:when test="$input-precipitation-rainfall and (not($merge-precipitation-rainfall) or $input-precipitation-rainfall/text() >= $merge-precipitation-rainfall/text())">
+ <xsl:copy>
+ <xsl:apply-templates select="$input-precipitation-rainfall/@*" />
+ <xsl:apply-templates select="$input-precipitation-rainfall/node()" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy>
+ <xsl:apply-templates select="$merge-precipitation-rainfall/@*" />
+ <xsl:apply-templates select="$merge-precipitation-rainfall/node()" />
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="precipitation[@class = 'extremeSnowfall']">
+ <xsl:variable name="month" select="../../@index" />
+ <xsl:variable name="day" select="../@index" />
+ <xsl:variable name="input-precipitation-snowfall" select="$input-root/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremeSnowfall']" />
+ <xsl:variable name="merge-precipitation-snowfall" select="$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremeSnowfall']" />
+
+ <xsl:choose>
+ <xsl:when test="$input-precipitation-snowfall and (not($merge-precipitation-snowfall) or $input-precipitation-snowfall/text() >= $merge-precipitation-snowfall/text())">
+ <xsl:copy>
+ <xsl:apply-templates select="$input-precipitation-snowfall/@*" />
+ <xsl:apply-templates select="$input-precipitation-snowfall/node()" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy>
+ <xsl:apply-templates select="$merge-precipitation-snowfall/@*" />
+ <xsl:apply-templates select="$merge-precipitation-snowfall/node()" />
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="precipitation[@class = 'extremePrecipitation']">
+ <xsl:variable name="month" select="../../@index" />
+ <xsl:variable name="day" select="../@index" />
+ <xsl:variable name="input-precipitation-extreme" select="$input-root/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremePrecipitation']" />
+ <xsl:variable name="merge-precipitation-extreme" select="$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremePrecipitation']" />
+
+ <xsl:choose>
+ <xsl:when test="$input-precipitation-extreme and (not($merge-precipitation-extreme) or $input-precipitation-extreme/text() >= $merge-precipitation-extreme/text())">
+ <xsl:copy>
+ <xsl:apply-templates select="$input-precipitation-extreme/@*" />
+ <xsl:apply-templates select="$input-precipitation-extreme/node()" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy>
+ <xsl:apply-templates select="$merge-precipitation-extreme/@*" />
+ <xsl:apply-templates select="$merge-precipitation-extreme/node()" />
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="precipitation[@class = 'extremeSnowOnGround']">
+ <xsl:variable name="month" select="../../@index" />
+ <xsl:variable name="day" select="../@index" />
+ <xsl:variable name="input-precipitation-snow-ground" select="$input-root/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremeSnowOnGround']" />
+ <xsl:variable name="merge-precipitation-snow-ground" select="$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremeSnowOnGround']" />
+
+ <xsl:choose>
+ <xsl:when test="$input-precipitation-snow-ground and (not($merge-precipitation-snow-ground) or $input-precipitation-snow-ground/text() >= $merge-precipitation-snow-ground/text())">
+ <xsl:copy>
+ <xsl:apply-templates select="$input-precipitation-snow-ground/@*" />
+ <xsl:apply-templates select="$input-precipitation-snow-ground/node()" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy>
+ <xsl:apply-templates select="$merge-precipitation-snow-ground/@*" />
+ <xsl:apply-templates select="$merge-precipitation-snow-ground/node()" />
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="pop">
+ <xsl:variable name="month" select="../../@index" />
+ <xsl:variable name="day" select="../@index" />
+ <xsl:variable name="input-pop" select="$input-root/climatedata/month[@index = $month]/day[@index = $day]/pop" />
+ <xsl:variable name="merge-pop" select="$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/pop" />
+
+ <xsl:choose>
+ <xsl:when test="$input-pop and $input-pop/text() != '' and $merge-pop and $merge-pop/text() != ''">
+ <xsl:copy>
+ <xsl:apply-templates select="$input-pop/@*" />
+ <xsl:attribute name="values-count"><xsl:value-of select="$input-pop/@values-count + 1" /></xsl:attribute>
+ <xsl:value-of select="format-number(number($input-pop/text() * $input-pop/@values-count + $merge-pop/text()) div number($input-pop/@values-count + 1), '0.0')" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:when test="$input-pop and (not($merge-pop) or $merge-pop/text() = '')">
+ <xsl:copy>
+ <xsl:apply-templates select="$input-pop/@*" />
+ <xsl:apply-templates select="$input-pop/node()" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:when test="not($input-pop) and $merge-pop and $merge-pop/text() != ''">
+ <xsl:copy>
+ <xsl:apply-templates select="$merge-pop/@*" />
+ <xsl:attribute name="values-count">1</xsl:attribute>
+ <xsl:apply-templates select="$merge-pop/node()" />
+ </xsl:copy>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="loop-day">
+ <xsl:param name="day" />
+
+ <xsl:variable name="input-day" select="$input-root/climatedata/month[@index = current()/@index]/day[@index = $day]" />
+ <xsl:variable name="merge-day" select="$merge-doc/climatedata/month[@index = current()/@index]/day[@index = $day]" />
+
+ <xsl:choose>
+ <xsl:when test="$input-day">
+ <xsl:apply-templates select="$input-day" />
+ </xsl:when>
+ <xsl:when test="$merge-day">
+ <xsl:apply-templates select="$merge-day" />
+ </xsl:when>
+ </xsl:choose>
+
+ <xsl:if test="$day < 31">
+ <xsl:call-template name="loop-day">
+ <xsl:with-param name="day" select="$day + 1" />
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:template>
+
+ <!-- Almanach files contain no flags -->
+ <xsl:template match="legend" />
+
+ <!-- Remove quality indicator -->
+ <xsl:template match="@quality" />
+</xsl:stylesheet>