Module:Climate profile
More actions
This is a documentation subpage for Climate profile. It contains usage information, categories, and other content that is not part of the original template.
This module shows the computed climate rows in {{Infobox Appellation}} and {{Infobox Growing Area}}: the Köppen class, the elevation and the five closest climate matches among the wiki's appellations and growing areas, each labelled as computed, with a line naming the datasets. It also gives the bare values the two infoboxes store. The method and the datasets are explained at Help:Climate matches.
The figures are not entered on pages. They sit on two data subpages, read with mw.loadData once per page render:
- Module:Climate profile/data: the profile and the five matches of every appellation and growing area.
- Module:Climate profile/normals: twelve monthly values (January first) of five climate variables for the same places, shown by Module:Climate year and read through
_normals. - Module:Climate profile/accessions: an index of Module:Climate profile/accessions/1 and the further numbered subpages, which hold the point, the computed Köppen class, the computed elevation and the same twelve monthly values for every accession with coordinates, each sampled at its own point. The accessions are split by page title so that a page loads one subpage of about 150 kB.
All are generated together from one computation over all places and accessions and are replaced together. They are not edited by hand: a change to one place would leave the similarity figures of the others inconsistent.
Functions
All functions are called with {{#invoke:Climate profile|function}}. The page is found by key= on the call, else by climate_key on the calling infobox, else by the current page title. A page with no entry gets an empty string from every function, so the infobox shows and stores nothing for it.
| Function | Returns |
|---|---|
has |
yes when the page has an entry, else empty. The infoboxes use it to show the Climate heading.
|
rows |
The infobox rows: Köppen class (computed), Elevation (computed), Closest climate matches, and the dataset line, each followed by a row separator. |
koppen |
The Köppen–Geiger class code, for Has computed Köppen class.
|
elevation |
The median elevation in metres, a bare number, for Has computed elevation.
|
matches |
The five match titles separated by ;, for Has climate match (stored with +sep=;).
|
count |
The number of places with an entry. |
From Lua, require('Module:Climate profile') gives _profile(key) (the entry), _matches(key) (an array of { title, similarity }, closest first), _normals(key) (a place's, else an accession's), _accession(key) (an accession's point, Köppen class, elevation and normals) and _meta().
Data fields
Each entry of places on the data subpage is keyed by page title.
| Field | Meaning |
|---|---|
kind |
appellation or growing area
|
s |
polygon (sampled inside the border) or point
|
lat, lon |
Centre of the border, or the point |
area |
Area inside the border, km² (absent for a point) |
k, ks |
Köppen–Geiger class covering the largest share of the sampled cells, and that share (0–1) |
e, e10, e90 |
Elevation in metres: median, 10th and 90th percentile |
es |
Elevation dataset: G GMTED2010 (1 km), C Copernicus GLO-90 (90 m)
|
bio5, bio6 |
Maximum temperature of the warmest month and minimum temperature of the coldest month, °C |
bio12, bio18 |
Annual precipitation and precipitation of the warmest quarter, mm |
ss |
bio18 ÷ bio12
|
ld |
Length of the longest day, hours |
m |
Five { title, similarity } pairs, closest first
|
meta holds the computation date, the dataset names and the standard deviations, means and weights used in the distance. On the normals subpage, each entry holds tmax and tmin (°C), pr (mm per month), vpd (Pa) and rsds (MJ m⁻² day⁻¹), twelve values each; meta.units repeats the units.
Tests
Module:Climate profile/testcases checks named places against the computation's own tables, recomputes every similarity from the stored values and checks the shape of every entry:
32 of 32 cases pass.
| Group | Case | Expected | Module | Result |
|---|---|---|---|---|
| Malana (appellation) | <nowiki>has</nowiki> | <nowiki>yes</nowiki> | <nowiki>yes</nowiki> | pass |
| Malana (appellation) | <nowiki>koppen</nowiki> | <nowiki>Dfc</nowiki> | <nowiki>Dfc</nowiki> | pass |
| Malana (appellation) | <nowiki>elevation</nowiki> | <nowiki>3171</nowiki> | <nowiki>3171</nowiki> | pass |
| Malana (appellation) | <nowiki>matches</nowiki> | <nowiki>Nirang (appellation);Mejigh (appellation);Waichin (appellation);Gohar (appellation);Thal (appellation)</nowiki> | <nowiki>Nirang (appellation);Mejigh (appellation);Waichin (appellation);Gohar (appellation);Thal (appellation)</nowiki> | pass |
| Malana (appellation) | <nowiki>Köppen text</nowiki> | <nowiki>Dfc (cold, no dry season, cold summer)</nowiki> | <nowiki>Dfc (cold, no dry season, cold summer)</nowiki> | pass |
| Malana (appellation) | <nowiki>elevation text</nowiki> | <nowiki>3,171 m (2,672–3,589 m)</nowiki> | <nowiki>3,171 m (2,672–3,589 m)</nowiki> | pass |
| Malana (appellation) | <nowiki>sources</nowiki> | <nowiki>Computed from CHELSA V2.1 (1981–2010), the Köppen–Geiger map of Beck et al. (1991–2020) and Copernicus GLO-90 (90 m) · About these figures</nowiki> | <nowiki>Computed from CHELSA V2.1 (1981–2010), the Köppen–Geiger map of Beck et al. (1991–2020) and Copernicus GLO-90 (90 m) · About these figures</nowiki> | pass |
| Saur Valley (appellation) | <nowiki>elevation text</nowiki> | <nowiki>1,548 m</nowiki> | <nowiki>1,548 m</nowiki> | pass |
| Saur Valley (appellation) | <nowiki>Köppen text</nowiki> | <nowiki>Cwa (temperate, dry winter, hot summer)</nowiki> | <nowiki>Cwa (temperate, dry winter, hot summer)</nowiki> | pass |
| Saur Valley (appellation) | <nowiki>matches text</nowiki> | <nowiki>Patkai Bum – 75 Kali Valley – 74 Liti (appellation) – 73 Naga Hills – 72 Upper Jaldhaka Valley – 71</nowiki> |
<nowiki>Patkai Bum – 75 Kali Valley – 74 Liti (appellation) – 73 Naga Hills – 72 Upper Jaldhaka Valley – 71</nowiki> |
pass |
| Tha Sala (appellation) | <nowiki>elevation text</nowiki> | <nowiki>14 m (5–35 m)</nowiki> | <nowiki>14 m (5–35 m)</nowiki> | pass |
| Tha Sala (appellation) | <nowiki>Köppen text</nowiki> | <nowiki>Af (tropical, rainforest)</nowiki> | <nowiki>Af (tropical, rainforest)</nowiki> | pass |
| Tha Sala (appellation) | <nowiki>sources</nowiki> | <nowiki>Computed from CHELSA V2.1 (1981–2010), the Köppen–Geiger map of Beck et al. (1991–2020) and GMTED2010 (1 km) · About these figures</nowiki> | <nowiki>Computed from CHELSA V2.1 (1981–2010), the Köppen–Geiger map of Beck et al. (1991–2020) and GMTED2010 (1 km) · About these figures</nowiki> | pass |
| Abdelghaya Souahel (appellation) | <nowiki>rows</nowiki> | Köppen class (computed) | Csa (temperate, dry summer, hot summer) | |
| Elevation (computed) | 1,061 m (681–1,536 m) | |||
| Closest climate matches | Beni Bouchibet (appellation) – 91 Ketama (appellation) – 89 Chefchaouen – 87 Beni Ahmed Imoukzan (appellation) – 86 Beqaa Valley – 77 | |||
| Computed from CHELSA V2.1 (1981–2010), the Köppen–Geiger map of Beck et al. (1991–2020) and GMTED2010 (1 km) · About these figures | ||||
| Csa (temperate, dry summer, hot summer) | ||||
| Elevation (computed) | 1,061 m (681–1,536 m) | |||
| Closest climate matches | Beni Bouchibet (appellation) – 91 Ketama (appellation) – 89 Chefchaouen – 87 Beni Ahmed Imoukzan (appellation) – 86 Beqaa Valley – 77 | |||
| Computed from CHELSA V2.1 (1981–2010), the Köppen–Geiger map of Beck et al. (1991–2020) and GMTED2010 (1 km) · About these figures | ||||
| Yu County, Hebei | <nowiki>koppen</nowiki> | <nowiki>BSk</nowiki> | <nowiki>BSk</nowiki> | pass |
| climate_key | <nowiki>parent argument</nowiki> | <nowiki>Dfc</nowiki> | <nowiki>Dfc</nowiki> | pass |
| No profile | <nowiki>has</nowiki> | <nowiki></nowiki> | <nowiki></nowiki> | pass |
| No profile | <nowiki>rows</nowiki> | <nowiki></nowiki> | <nowiki></nowiki> | pass |
| No profile | <nowiki>koppen</nowiki> | <nowiki></nowiki> | <nowiki></nowiki> | pass |
| No profile | <nowiki>elevation</nowiki> | <nowiki></nowiki> | <nowiki></nowiki> | pass |
| No profile | <nowiki>matches</nowiki> | <nowiki></nowiki> | <nowiki></nowiki> | pass |
| Whole data | <nowiki>places</nowiki> | <nowiki>377</nowiki> | <nowiki>377</nowiki> | pass |
| Whole data | <nowiki>places with exactly five matches</nowiki> | <nowiki>377</nowiki> | <nowiki>377</nowiki> | pass |
| Whole data | <nowiki>matches naming a place with no profile</nowiki> | <nowiki>0</nowiki> | <nowiki>0</nowiki> | pass |
| Whole data | <nowiki>places listed as their own match</nowiki> | <nowiki>0</nowiki> | <nowiki>0</nowiki> | pass |
| Whole data | <nowiki>similarities rising down a list</nowiki> | <nowiki>0</nowiki> | <nowiki>0</nowiki> | pass |
| Whole data | <nowiki>Köppen classes in the legend</nowiki> | <nowiki>377</nowiki> | <nowiki>377</nowiki> | pass |
| Whole data | <nowiki>medians inside their 10th–90th range</nowiki> | <nowiki>377</nowiki> | <nowiki>377</nowiki> | pass |
| Whole data | <nowiki>places with twelve months of all five normals</nowiki> | <nowiki>377</nowiki> | <nowiki>377</nowiki> | pass |
| Whole data | <nowiki>similarities more than 1 point off when recomputed</nowiki> | <nowiki>0</nowiki> | <nowiki>0</nowiki> | pass |
| Whole data | <nowiki>similarities 1 point off (rounding; shown, not failed)</nowiki> | <nowiki>–</nowiki> | <nowiki>2</nowiki> | pass |
| Whole data | <nowiki>count()</nowiki> | <nowiki>377</nowiki> | <nowiki>377</nowiki> | pass |
-- Module:Climate profile
-- Computed climate rows for {{Infobox Appellation}} and {{Infobox Growing Area}}: the Köppen–Geiger class,
-- the elevation and the five closest climate matches among the wiki's appellations and growing areas.
-- The figures are on Module:Climate profile/data (and the monthly normals on Module:Climate profile/normals);
-- the method and datasets are on Help:Climate matches. Functions and fields: Module:Climate profile/doc.
local p = {}
p.DATA = 'Module:Climate profile/data'
p.NORMALS = 'Module:Climate profile/normals'
p.ACCESSIONS = 'Module:Climate profile/accessions'
local HELP = 'Help:Climate matches'
local MATCHES = 5
-- Beck et al. (2023) legend.
local KOPPEN = {
Af = 'tropical, rainforest', Am = 'tropical, monsoon', Aw = 'tropical, savannah',
BWh = 'arid, desert, hot', BWk = 'arid, desert, cold', BSh = 'arid, steppe, hot', BSk = 'arid, steppe, cold',
Csa = 'temperate, dry summer, hot summer', Csb = 'temperate, dry summer, warm summer',
Csc = 'temperate, dry summer, cold summer',
Cwa = 'temperate, dry winter, hot summer', Cwb = 'temperate, dry winter, warm summer',
Cwc = 'temperate, dry winter, cold summer',
Cfa = 'temperate, no dry season, hot summer', Cfb = 'temperate, no dry season, warm summer',
Cfc = 'temperate, no dry season, cold summer',
Dsa = 'cold, dry summer, hot summer', Dsb = 'cold, dry summer, warm summer', Dsc = 'cold, dry summer, cold summer',
Dsd = 'cold, dry summer, very cold winter',
Dwa = 'cold, dry winter, hot summer', Dwb = 'cold, dry winter, warm summer', Dwc = 'cold, dry winter, cold summer',
Dwd = 'cold, dry winter, very cold winter',
Dfa = 'cold, no dry season, hot summer', Dfb = 'cold, no dry season, warm summer',
Dfc = 'cold, no dry season, cold summer', Dfd = 'cold, no dry season, very cold winter',
ET = 'polar, tundra', EF = 'polar, frost',
}
p.KOPPEN = KOPPEN
local function trim(s)
if s == nil then return nil end
s = mw.text.trim(tostring(s))
return s ~= '' and s or nil
end
-- The page whose figures are wanted: key= on the #invoke, else climate_key= on the calling template,
-- else the current page.
local function getKey(frame)
local k = trim(frame.args.key)
if k then return k end
local parent = frame:getParent()
if parent then
k = trim(parent.args.climate_key)
if k then return k end
end
return mw.title.getCurrentTitle().text
end
-- Lua API -----------------------------------------------------------------------------------------
-- The stored profile of a place (a read-only table), or nil.
function p._profile(key)
if not key then return nil end
return mw.loadData(p.DATA).places[key]
end
-- The twelve monthly normals of a place or an accession: { tmax = {…12}, tmin, pr, vpd, rsds }, or nil. Units: the
-- meta table of Module:Climate profile/normals. Places are looked up first; the accessions subpage is loaded only
-- for a key that is not a place.
function p._normals(key)
if not key then return nil end
return mw.loadData(p.NORMALS).places[key] or p._accession(key)
end
-- The stored point, Köppen class, elevation and normals of an accession (a read-only table), or nil. The
-- accessions are split by title into chunks; the index names the first title of each, so one chunk is loaded.
function p._accession(key)
if not key then return nil end
local chunks = mw.loadData(p.ACCESSIONS).chunks
local page
local i = 1
while chunks[i] do
if chunks[i].first <= key then page = chunks[i].page else break end
i = i + 1
end
if not page then return nil end
return mw.loadData(page).accessions[key]
end
function p._meta()
return mw.loadData(p.DATA).meta
end
-- The matches of a place as a plain array of { title = …, similarity = … }, closest first.
function p._matches(key)
local e = p._profile(key)
local out = {}
if not e then return out end
for i = 1, MATCHES do
local m = e.m[i]
if not m then break end
out[#out + 1] = { title = m[1], similarity = m[2] }
end
return out
end
-- Formatting --------------------------------------------------------------------------------------
local function fmt(n)
return mw.language.getContentLanguage():formatNum(math.floor(n + 0.5))
end
function p._koppenText(e)
local d = KOPPEN[e.k]
return d and (e.k .. ' (' .. d .. ')') or e.k
end
function p._elevationText(e)
if e.e10 == e.e90 then
return fmt(e.e) .. ' m'
end
return fmt(e.e) .. ' m (' .. fmt(e.e10) .. '–' .. fmt(e.e90) .. ' m)'
end
function p._matchesText(key)
local lines = {}
for _, m in ipairs(p._matches(key)) do
lines[#lines + 1] = '[[' .. m.title .. ']] – <span title="Similarity, out of 100">'
.. m.similarity .. '</span>'
end
return table.concat(lines, '<br>')
end
function p._sourcesText(e)
local meta = p._meta()
return 'Computed from ' .. meta.climate .. ', ' .. meta.koppen .. ' and '
.. (meta.elevation[e.es] or e.es) .. ' · [[' .. HELP .. '|About these figures]]'
end
-- Infobox rows, each followed by a row separator; empty when the page has no profile.
function p._rows(key)
local e = p._profile(key)
if not e then return '' end
local o = {}
local function row(label, value)
o[#o + 1] = '! class="lw-infobox__label" | ' .. label .. '\n| class="lw-infobox__value" | ' .. value .. '\n|-'
end
if e.k and e.k ~= '' then row('Köppen class (computed)', p._koppenText(e)) end
row('Elevation (computed)', p._elevationText(e))
local m = p._matchesText(key)
if m ~= '' then row('Closest climate matches', m) end
o[#o + 1] = '| colspan="2" class="lw-infobox__caption" | ' .. p._sourcesText(e) .. '\n|-'
return table.concat(o, '\n')
end
-- #invoke functions -------------------------------------------------------------------------------
-- "yes" when the page has a profile, else empty (for the infobox's Climate header).
function p.has(frame)
return p._profile(getKey(frame)) and 'yes' or ''
end
function p.rows(frame)
return p._rows(getKey(frame))
end
-- Bare values for {{#set:}}.
function p.koppen(frame)
local e = p._profile(getKey(frame))
return e and e.k or ''
end
function p.elevation(frame)
local e = p._profile(getKey(frame))
return e and tostring(math.floor(e.e + 0.5)) or ''
end
-- Match titles separated by ";" (the #set uses |+sep=;).
function p.matches(frame)
local t = {}
for _, m in ipairs(p._matches(getKey(frame))) do t[#t + 1] = m.title end
return table.concat(t, ';')
end
-- The number of places with a profile.
function p.count(frame)
local n = 0
for _ in pairs(mw.loadData(p.DATA).places) do n = n + 1 end
return tostring(n)
end
return p