]> Wikimedia Canada | Git repositories - eccc_to_commons.git/commitdiff
Add ECCC almanach XML merger
authorPierre Choffet <peuc@wanadoo.fr>
Wed, 12 May 2021 15:36:16 +0000 (11:36 -0400)
committerPierre Choffet <peuc@wanadoo.fr>
Wed, 12 May 2021 15:36:16 +0000 (11:36 -0400)
eccc_merger_almanach.xslt [new file with mode: 0644]

diff --git a/eccc_merger_almanach.xslt b/eccc_merger_almanach.xslt
new file mode 100644 (file)
index 0000000..010a97f
--- /dev/null
@@ -0,0 +1,350 @@
+<?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() &gt;= $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() &lt;= 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() &gt;= $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() &lt;= $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() &gt;= $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() &gt;= $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() &gt;= $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() &gt;= $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 &lt; 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>