1 <?xml version=
"1.0" encoding=
"UTF-8"?>
3 <!-- eccc_merger.xslt - Merge XML historical data as provided by
4 Environment and Climate Change Canada
5 Copyright (C) 2020 Pierre Choffet
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 Rounding is always done one digit after decimal point.
23 Output data is calculated as such:
24 temperature.extremeMax : Greatest of all values
25 temperature.extremeMin : Lowest of all values
26 temperature.normalMax : Greatest of all values
27 temperature.normalMin : Lowest of all values
28 temperature.normalMean : Weighted mean of all values
29 precipitation.extremeRainfall : Greatest of all values
30 precipitation.extremeSnowfall : Greatest of all values
31 precipitation.extremePrecipitation: Greatest of all values
32 precipitation.extremeSnowOnGround : Greatest of all values
33 pop : Weighted mean of all values
36 <xsl:stylesheet version=
"1.0" xmlns:
xsl=
"http://www.w3.org/1999/XSL/Transform"
37 xmlns:
xsi=
"http://www.w3.org/2001/XMLSchema-instance"
38 exclude-result-prefixes=
"xsi">
40 <xsl:output method=
"xml" encoding=
"utf-8" />
42 <xsl:variable name=
"input-root" select=
"/" />
43 <xsl:variable name=
"merge-doc" select=
"document($merge-path)" />
46 <xsl:template match=
"@*|node()">
48 <xsl:apply-templates select=
"@*|node()" />
52 <xsl:template match=
"/climatedata">
54 <!-- Upon first merge, import missing metadata elements -->
55 <xsl:if test=
"not(lang)">
56 <xsl:apply-templates select=
"$merge-doc/climatedata/lang" />
58 <xsl:if test=
"not(stationinformation)">
59 <xsl:apply-templates select=
"$merge-doc/climatedata/stationinformation" />
62 <xsl:apply-templates select=
"@*|node()" />
66 <xsl:template match=
"month">
68 <xsl:apply-templates select=
"@index" />
70 <!-- ECCC data may not contain all days element, so we're counting by ourserves -->
71 <xsl:call-template name=
"loop-day">
72 <xsl:with-param name=
"day" select=
"1" />
77 <xsl:template match=
"day">
79 <xsl:attribute name=
"index">
80 <xsl:value-of select=
"@index" />
83 <xsl:apply-templates match=
"node()" />
87 <xsl:template match=
"temperature[@class = 'extremeMax']">
88 <xsl:variable name=
"month" select=
"../../@index" />
89 <xsl:variable name=
"day" select=
"../@index" />
90 <xsl:variable name=
"input-extreme-max" select=
"$input-root/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'extremeMax']" />
91 <xsl:variable name=
"merge-extreme-max" select=
"$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'extremeMax']" />
94 <!-- Both nodes values are numeric and input value is greater or equal than merge
95 or Input exists but merge doesn't or is not numeric
97 <xsl:when test=
"(string(number($input-extreme-max)) != 'NaN' and string(number($merge-extreme-max) != 'NaN') and $input-extreme-max >= $merge-extreme-max) or ($input-extreme-max and (string(number($merge-extreme-max)) = 'NaN'))">
99 <xsl:apply-templates select=
"$input-extreme-max/@*" />
100 <xsl:apply-templates select=
"$input-extreme-max/node()" />
103 <!-- Both nodes values are numeric and input value is lesser than merge
104 or Input doesn't exist or is not numeric but merge does
106 <xsl:when test=
"(string(number($input-extreme-max)) != 'NaN' and string(number($merge-extreme-max) != 'NaN') and $input-extreme-max < $merge-extreme-max) or ((string(number($input-extreme-max)) = 'NaN') and $merge-extreme-max)">
108 <xsl:apply-templates select=
"$merge-extreme-max/@*" />
109 <xsl:apply-templates select=
"$merge-extreme-max/node()" />
113 <xsl:message terminate=
"no">
114 <xsl:value-of select=
"concat($month, '-', $day, ' - extremeMax ', number($input-extreme-max), ' ', number($merge-extreme-max))" />
116 <xsl:message terminate=
"yes">Trapping case not supposed to happen.
</xsl:message>
121 <xsl:template match=
"temperature[@class = 'extremeMin']">
122 <xsl:variable name=
"month" select=
"../../@index" />
123 <xsl:variable name=
"day" select=
"../@index" />
124 <xsl:variable name=
"input-extreme-min" select=
"$input-root/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'extremeMin']" />
125 <xsl:variable name=
"merge-extreme-min" select=
"$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'extremeMin']" />
128 <!-- Both nodes values are numeric and input value is lesser or equal than merge
129 or Input exists but merge doesn't or is not numeric
131 <xsl:when test=
"(string(number($input-extreme-min)) != 'NaN' and string(number($merge-extreme-min) != 'NaN') and $input-extreme-min <= $merge-extreme-min) or ($input-extreme-min and (string(number($merge-extreme-min)) = 'NaN'))">
133 <xsl:apply-templates select=
"$input-extreme-min/@*" />
134 <xsl:apply-templates select=
"$input-extreme-min/node()" />
137 <!-- Both nodes values are numeric and input value is greater than merge
138 or Input doesn't exist or is not numeric but merge does
140 <xsl:when test=
"(string(number($input-extreme-min)) != 'NaN' and string(number($merge-extreme-min) != 'NaN') and $input-extreme-min > $merge-extreme-min) or ((string(number($input-extreme-min)) = 'NaN') and $merge-extreme-min)">
142 <xsl:apply-templates select=
"$merge-extreme-min/@*" />
143 <xsl:apply-templates select=
"$merge-extreme-min/node()" />
147 <xsl:message terminate=
"no">
148 <xsl:value-of select=
"concat($month, '-', $day, ' - extremeMin ', number($input-extreme-min), ' ', number($merge-extreme-min))" />
150 <xsl:message terminate=
"yes">Trapping case not supposed to happen.
</xsl:message>
155 <xsl:template match=
"temperature[@class = 'normalMax']">
156 <xsl:variable name=
"month" select=
"../../@index" />
157 <xsl:variable name=
"day" select=
"../@index" />
158 <xsl:variable name=
"input-normal-max" select=
"$input-root/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'normalMax']" />
159 <xsl:variable name=
"merge-normal-max" select=
"$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'normalMax']" />
162 <!-- Both nodes values are numeric and input value is greater or equal than merge
163 or Input exists but merge doesn't or is not numeric
165 <xsl:when test=
"(string(number($input-normal-max)) != 'NaN' and string(number($merge-normal-max) != 'NaN') and $input-normal-max >= $merge-normal-max) or ($input-normal-max and (string(number($merge-normal-max)) = 'NaN'))">
167 <xsl:apply-templates select=
"$input-normal-max/@*" />
168 <xsl:apply-templates select=
"$input-normal-max/node()" />
171 <!-- Both nodes values are numeric and input value is lesser than merge
172 or Input doesn't exist or is not numeric but merge does
174 <xsl:when test=
"(string(number($input-normal-max)) != 'NaN' and string(number($merge-normal-max) != 'NaN') and $input-normal-max < $merge-normal-max) or ((string(number($input-normal-max)) = 'NaN') and $merge-normal-max)">
176 <xsl:apply-templates select=
"$merge-normal-max/@*" />
177 <xsl:apply-templates select=
"$merge-normal-max/node()" />
181 <xsl:message terminate=
"no">
182 <xsl:value-of select=
"concat($month, '-', $day, ' - normalMax ', number($input-normal-max), ' ', number($merge-normal-max))" />
184 <xsl:message terminate=
"yes">Trapping case not supposed to happen.
</xsl:message>
189 <xsl:template match=
"temperature[@class = 'normalMin']">
190 <xsl:variable name=
"month" select=
"../../@index" />
191 <xsl:variable name=
"day" select=
"../@index" />
192 <xsl:variable name=
"input-normal-min" select=
"$input-root/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'normalMin']" />
193 <xsl:variable name=
"merge-normal-min" select=
"$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'normalMin']" />
196 <!-- Both nodes values are numeric and input value is lesser or equal than merge
197 or Input exists but merge doesn't or is not numeric
199 <xsl:when test=
"(string(number($input-normal-min)) != 'NaN' and string(number($merge-normal-min) != 'NaN') and $input-normal-min <= $merge-normal-min) or ($input-normal-min and (string(number($merge-normal-min)) = 'NaN'))">
201 <xsl:apply-templates select=
"$input-normal-min/@*" />
202 <xsl:apply-templates select=
"$input-normal-min/node()" />
205 <!-- Both nodes values are numeric and input value is greater than merge
206 or Input doesn't exist or is not numeric but merge does
208 <xsl:when test=
"(string(number($input-normal-min)) != 'NaN' and string(number($merge-normal-min) != 'NaN') and $input-normal-min > $merge-normal-min) or ((string(number($input-normal-min)) = 'NaN') and $merge-normal-min)">
210 <xsl:apply-templates select=
"$merge-normal-min/@*" />
211 <xsl:apply-templates select=
"$merge-normal-min/node()" />
215 <xsl:message terminate=
"no">
216 <xsl:value-of select=
"concat($month, '-', $day, ' - normalMin ', number($input-normal-min), ' ', number($merge-normal-min))" />
218 <xsl:message terminate=
"yes">Trapping case not supposed to happen.
</xsl:message>
223 <xsl:template match=
"temperature[@class = 'normalMean']">
224 <xsl:variable name=
"month" select=
"../../@index" />
225 <xsl:variable name=
"day" select=
"../@index" />
226 <xsl:variable name=
"input-normal-mean" select=
"$input-root/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'normalMean']" />
227 <xsl:variable name=
"merge-normal-mean" select=
"$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/temperature[@class = 'normalMean']" />
230 <!-- Node exists and are not empty on both sides -->
231 <xsl:when test=
"$input-normal-mean and $input-normal-mean != '' and $merge-normal-mean and $merge-normal-mean/text() != ''">
233 <xsl:apply-templates select=
"$input-normal-mean/@*" />
234 <xsl:attribute name=
"values-count"><xsl:value-of select=
"$input-normal-mean/@values-count + 1" /></xsl:attribute>
235 <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')" />
238 <!-- Node exists but are empty on both sides -->
239 <xsl:when test=
"$input-normal-mean = '' and $merge-normal-mean = ''">
241 <xsl:apply-templates select=
"$input-normal-mean/@*" />
242 <xsl:apply-templates select=
"$input-normal-mean/node()" />
245 <!-- Node exists in input but not-or-empty in merge -->
246 <xsl:when test=
"$input-normal-mean and (not($merge-normal-mean) or $merge-normal-mean = '')">
248 <xsl:apply-templates select=
"$input-normal-mean/@*" />
249 <xsl:apply-templates select=
"$input-normal-mean/node()" />
252 <!-- Node doesn't exist or is empty in input and exists in merge -->
253 <xsl:when test=
"(not($input-normal-mean) or $input-normal-mean = '') and $merge-normal-mean">
255 <xsl:apply-templates select=
"$merge-normal-mean/@*" />
256 <xsl:if test=
"$merge-normal-mean != ''">
257 <xsl:attribute name=
"values-count">1</xsl:attribute>
258 <xsl:apply-templates select=
"$merge-normal-mean/node()" />
263 <xsl:message terminate=
"no">
264 <xsl:value-of select=
"concat($month, '-', $day, ' - normalMean: ', $input-normal-mean, ' ', $merge-normal-mean)" />
266 <xsl:message terminate=
"yes">Trapping case not supposed to happen.
</xsl:message>
271 <xsl:template match=
"precipitation[@class = 'extremeRainfall']">
272 <xsl:variable name=
"month" select=
"../../@index" />
273 <xsl:variable name=
"day" select=
"../@index" />
274 <xsl:variable name=
"input-precipitation-rainfall" select=
"$input-root/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremeRainfall']" />
275 <xsl:variable name=
"merge-precipitation-rainfall" select=
"$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremeRainfall']" />
278 <!-- Both nodes values are numeric and input value is greater or equal than merge
279 or Input exists but merge doesn't or is not numeric
281 <xsl:when test=
"(string(number($input-precipitation-rainfall)) != 'NaN' and string(number($merge-precipitation-rainfall) != 'NaN') and $input-precipitation-rainfall >= $merge-precipitation-rainfall) or ($input-precipitation-rainfall and (string(number($merge-precipitation-rainfall)) = 'NaN'))">
283 <xsl:apply-templates select=
"$input-precipitation-rainfall/@*" />
284 <xsl:apply-templates select=
"$input-precipitation-rainfall/node()" />
287 <!-- Both nodes values are numeric and input value is lesser than merge
288 or Input doesn't exist or is not numeric but merge does
290 <xsl:when test=
"(string(number($input-precipitation-rainfall)) != 'NaN' and string(number($merge-precipitation-rainfall) != 'NaN') and $input-precipitation-rainfall < $merge-precipitation-rainfall) or ((string(number($input-precipitation-rainfall)) = 'NaN') and $merge-precipitation-rainfall)">
292 <xsl:apply-templates select=
"$merge-precipitation-rainfall/@*" />
293 <xsl:apply-templates select=
"$merge-precipitation-rainfall/node()" />
297 <xsl:message terminate=
"no">
298 <xsl:value-of select=
"concat($month, '-', $day, ' - extremeRainfall ', number($input-precipitation-rainfall), ' ', number($merge-precipitation-rainfall))" />
300 <xsl:message terminate=
"yes">Trapping case not supposed to happen.
</xsl:message>
305 <xsl:template match=
"precipitation[@class = 'extremeSnowfall']">
306 <xsl:variable name=
"month" select=
"../../@index" />
307 <xsl:variable name=
"day" select=
"../@index" />
308 <xsl:variable name=
"input-precipitation-snowfall" select=
"$input-root/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremeSnowfall']" />
309 <xsl:variable name=
"merge-precipitation-snowfall" select=
"$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremeSnowfall']" />
312 <!-- Both nodes values are numeric and input value is greater or equal than merge
313 or Input exists but merge doesn't or is not numeric
315 <xsl:when test=
"(string(number($input-precipitation-snowfall)) != 'NaN' and string(number($merge-precipitation-snowfall) != 'NaN') and $input-precipitation-snowfall >= $merge-precipitation-snowfall) or ($input-precipitation-snowfall and (string(number($merge-precipitation-snowfall)) = 'NaN'))">
317 <xsl:apply-templates select=
"$input-precipitation-snowfall/@*" />
318 <xsl:apply-templates select=
"$input-precipitation-snowfall/node()" />
321 <!-- Both nodes values are numeric and input value is lesser than merge
322 or Input doesn't exist or is not numeric but merge does
324 <xsl:when test=
"(string(number($input-precipitation-snowfall)) != 'NaN' and string(number($merge-precipitation-snowfall) != 'NaN') and $input-precipitation-snowfall < $merge-precipitation-snowfall) or ((string(number($input-precipitation-snowfall)) = 'NaN') and $merge-precipitation-snowfall)">
326 <xsl:apply-templates select=
"$merge-precipitation-snowfall/@*" />
327 <xsl:apply-templates select=
"$merge-precipitation-snowfall/node()" />
331 <xsl:message terminate=
"no">
332 <xsl:value-of select=
"concat($month, '-', $day, ' - extremeSnowfall ', number($input-precipitation-snowfall), ' ', number($merge-precipitation-snowfall))" />
334 <xsl:message terminate=
"yes">Trapping case not supposed to happen.
</xsl:message>
339 <xsl:template match=
"precipitation[@class = 'extremePrecipitation']">
340 <xsl:variable name=
"month" select=
"../../@index" />
341 <xsl:variable name=
"day" select=
"../@index" />
342 <xsl:variable name=
"input-precipitation-extreme" select=
"$input-root/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremePrecipitation']" />
343 <xsl:variable name=
"merge-precipitation-extreme" select=
"$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremePrecipitation']" />
346 <!-- Both nodes values are numeric and input value is greater or equal than merge
347 or Input exists but merge doesn't or is not numeric
349 <xsl:when test=
"(string(number($input-precipitation-extreme)) != 'NaN' and string(number($merge-precipitation-extreme) != 'NaN') and $input-precipitation-extreme >= $merge-precipitation-extreme) or ($input-precipitation-extreme and (string(number($merge-precipitation-extreme)) = 'NaN'))">
351 <xsl:apply-templates select=
"$input-precipitation-extreme/@*" />
352 <xsl:apply-templates select=
"$input-precipitation-extreme/node()" />
355 <!-- Both nodes values are numeric and input value is lesser than merge
356 or Input doesn't exist or is not numeric but merge does
358 <xsl:when test=
"(string(number($input-precipitation-extreme)) != 'NaN' and string(number($merge-precipitation-extreme) != 'NaN') and $input-precipitation-extreme < $merge-precipitation-extreme) or ((string(number($input-precipitation-extreme)) = 'NaN') and $merge-precipitation-extreme)">
360 <xsl:apply-templates select=
"$merge-precipitation-extreme/@*" />
361 <xsl:apply-templates select=
"$merge-precipitation-extreme/node()" />
365 <xsl:message terminate=
"no">
366 <xsl:value-of select=
"concat($month, '-', $day, ' - extremePrecipitation ', number($input-precipitation-extreme), ' ', number($merge-precipitation-extreme))" />
368 <xsl:message terminate=
"yes">Trapping case not supposed to happen.
</xsl:message>
373 <xsl:template match=
"precipitation[@class = 'extremeSnowOnGround']">
374 <xsl:variable name=
"month" select=
"../../@index" />
375 <xsl:variable name=
"day" select=
"../@index" />
376 <xsl:variable name=
"input-precipitation-snow-ground" select=
"$input-root/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremeSnowOnGround']" />
377 <xsl:variable name=
"merge-precipitation-snow-ground" select=
"$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/precipitation[@class = 'extremeSnowOnGround']" />
380 <!-- Both nodes values are numeric and input value is greater or equal than merge
381 or Input exists but merge doesn't or is not numeric
383 <xsl:when test=
"(string(number($input-precipitation-snow-ground)) != 'NaN' and string(number($merge-precipitation-snow-ground) != 'NaN') and $input-precipitation-snow-ground >= $merge-precipitation-snow-ground) or ($input-precipitation-snow-ground and (string(number($merge-precipitation-snow-ground)) = 'NaN'))">
385 <xsl:apply-templates select=
"$input-precipitation-snow-ground/@*" />
386 <xsl:apply-templates select=
"$input-precipitation-snow-ground/node()" />
389 <!-- Both nodes values are numeric and input value is lesser than merge
390 or Input doesn't exist or is not numeric but merge does
392 <xsl:when test=
"(string(number($input-precipitation-snow-ground)) != 'NaN' and string(number($merge-precipitation-snow-ground) != 'NaN') and $input-precipitation-snow-ground < $merge-precipitation-snow-ground) or ((string(number($input-precipitation-snow-ground)) = 'NaN') and $merge-precipitation-snow-ground)">
394 <xsl:apply-templates select=
"$merge-precipitation-snow-ground/@*" />
395 <xsl:apply-templates select=
"$merge-precipitation-snow-ground/node()" />
399 <xsl:message terminate=
"no">
400 <xsl:value-of select=
"concat($month, '-', $day, ' - extremeSnowOnGround ', number($input-precipitation-snow-ground), ' ', number($merge-precipitation-snow-ground))" />
402 <xsl:message terminate=
"yes">Trapping case not supposed to happen.
</xsl:message>
407 <xsl:template match=
"pop">
408 <xsl:variable name=
"month" select=
"../../@index" />
409 <xsl:variable name=
"day" select=
"../@index" />
410 <xsl:variable name=
"input-pop" select=
"$input-root/climatedata/month[@index = $month]/day[@index = $day]/pop" />
411 <xsl:variable name=
"merge-pop" select=
"$merge-doc/climatedata/month[@index = $month]/day[@index = $day]/pop" />
414 <!-- Node exists and are not empty on both sides -->
415 <xsl:when test=
"$input-pop and $input-pop != '' and $merge-pop and $merge-pop/text() != ''">
417 <xsl:apply-templates select=
"$input-pop/@*" />
418 <xsl:attribute name=
"values-count"><xsl:value-of select=
"$input-pop/@values-count + 1" /></xsl:attribute>
419 <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')" />
422 <!-- Node exists but are empty on both sides -->
423 <xsl:when test=
"$input-pop = '' and $merge-pop = ''">
425 <xsl:apply-templates select=
"$input-pop/@*" />
426 <xsl:apply-templates select=
"$input-pop/node()" />
429 <!-- Node exists in input but not-or-empty in merge -->
430 <xsl:when test=
"$input-pop and (not($merge-pop) or $merge-pop = '')">
432 <xsl:apply-templates select=
"$input-pop/@*" />
433 <xsl:apply-templates select=
"$input-pop/node()" />
436 <!-- Node doesn't exist or is empty in input and exists in merge -->
437 <xsl:when test=
"(not($input-pop) or $input-pop = '') and $merge-pop">
439 <xsl:apply-templates select=
"$merge-pop/@*" />
440 <xsl:if test=
"$merge-pop != ''">
441 <xsl:attribute name=
"values-count">1</xsl:attribute>
442 <xsl:apply-templates select=
"$merge-pop/node()" />
447 <xsl:message terminate=
"no">
448 <xsl:value-of select=
"concat($month, '-', $day, ' - pop: ', $input-pop, ' ', $merge-pop)" />
450 <xsl:message terminate=
"yes">Trapping case not supposed to happen.
</xsl:message>
455 <xsl:template name=
"loop-day">
456 <xsl:param name=
"day" />
458 <xsl:variable name=
"input-day" select=
"$input-root/climatedata/month[@index = current()/@index]/day[@index = $day]" />
459 <xsl:variable name=
"merge-day" select=
"$merge-doc/climatedata/month[@index = current()/@index]/day[@index = $day]" />
462 <xsl:when test=
"$input-day">
463 <xsl:apply-templates select=
"$input-day" />
465 <xsl:when test=
"$merge-day">
466 <xsl:apply-templates select=
"$merge-day" />
470 <xsl:if test=
"$day < 31">
471 <xsl:call-template name=
"loop-day">
472 <xsl:with-param name=
"day" select=
"$day + 1" />
477 <!-- Almanach files contain no flags -->
478 <xsl:template match=
"legend" />
480 <!-- Remove quality indicator -->
481 <xsl:template match=
"@quality" />