@verifyhash/koppen-classifier
v0.1.1
Published
Zero-dependency Köppen–Geiger climate classifier from 12 monthly mean-temperature and precipitation values (hemisphere-aware).
Maintainers
Readme
koppen-classifier
A tiny, zero-dependency Node library that assigns a location its
Köppen–Geiger climate class (e.g. Cfa, BWh, Dfb) from twelve monthly
mean temperatures and twelve monthly precipitation totals. Pure function, no
network, no files, no state — give it numbers, get back a code.
The classification logic is a direct port of the proven koppenClass()
that has run in production on weatherhack.com to
label real cities from ERA5 normals. It was lifted, not re-derived, so its
boundaries match what that site already ships.
Who it's for
Anyone who has monthly climate normals and wants the Köppen letter code without pulling in a heavy geodata package: map/dashboard builders, teaching material, data-pipeline enrichment, quiz/trivia generators, worldbuilding tools.
Install / use
No install step for local use — it's one file with no dependencies. Copy the
folder in, or require('./koppen-classifier').
const { classify } = require('@verifyhash/koppen-classifier');
// London, UK — monthly arrays run January → December.
const london = classify({
tempsC: [5.2, 5.3, 7.6, 9.6, 12.9, 16.0, 18.1, 17.8, 15.2, 11.4, 7.8, 5.5],
precipMm: [55, 41, 42, 44, 49, 45, 45, 50, 49, 69, 59, 55],
lat: 51.5,
});
console.log(london.code); // 'Cfb'
console.log(london.label); // 'temperate oceanic'
console.log(london.groupLabel); // 'Temperate'API
classify(options) → result
options
| field | type | required | notes |
|--------------|------------|----------|-------|
| tempsC | number[] | yes | 12 monthly mean temperatures in °C, Jan → Dec. |
| precipMm | number[] | yes | 12 monthly precipitation totals in mm, Jan → Dec. |
| lat | number | one of these | Signed latitude, north positive. Only its sign is used, to pick the warm half-year. |
| hemisphere | string | one of these | 'N' / 'S' (also 'north' / 'south'). Alternative to lat. |
Provide either lat or hemisphere. If both are present, lat wins.
Invalid input (arrays not length 12, non-finite numbers, no hemisphere given)
throws a TypeError rather than returning a wrong answer.
Note: pass the monthly mean temperature. If your source has separate daily highs and lows, average them first:
mean = (high + low) / 2. That is exactly what weatherhack does before calling the original function.
result (a plain object)
| field | type | example | meaning |
|--------------|----------|------------------------|---------|
| code | string | 'Cfb' | Köppen–Geiger class code. |
| label | string | 'temperate oceanic' | Human-readable class name. |
| group | string | 'C' | Top-level group letter. |
| groupLabel | string | 'Temperate' | Top-level group name (Tropical / Arid / Temperate / Continental / Polar). |
| blurb | string | 'A coldest month …' | One-sentence plain-language summary. |
Lookup tables
KOPPEN_NAMES— map from every Köppen code to its human-readable name, e.g.KOPPEN_NAMES.Cfa === 'humid subtropical'— 31 codes in total.GROUP_NAMES— the five top-level groups:{ A: 'Tropical', B: 'Arid', C: 'Temperate', D: 'Continental', E: 'Polar' }.
What the algorithm actually does
It applies the standard Köppen decision tree, tested in this order:
- B (arid) first, because aridity overrides temperature. The dryness
threshold is
P_th = 20·MAT + offset, where the offset is 280 if ≥70% of annual precipitation falls in the warm half-year, 140 if 30–70%, else 0.BW(desert) when annual precip< ½·P_th, otherwiseBS(steppe); theh/ksplit is at a mean annual temperature of 18 °C. - A (tropical) when the coldest month averages ≥ 18 °C (
Af/Am/Aw/As). - E (polar) when the warmest month is below 10 °C (
ETtundra,EFice cap). - C vs D by the 0 °C coldest-month isotherm, then a seasonal-precip
third letter (
f/s/w) and a summer-heat fourth letter (a/b/c/d).
The warm half-year is April–September in the northern hemisphere and
October–March in the southern — this is why the hemisphere argument matters, and
why a southern-hemisphere Mediterranean city (dry December–February summer)
comes out Cs… rather than Cw….
Honest limits
- This is the common textbook variant of the boundaries. Other published
variants differ on edge rules (e.g. the exact aridity offset, or a 22 °C vs
0 °C
h/ksplit, or the As/Aw threshold). Results near a boundary can legitimately differ from other tools; this one matches weatherhack's output. - It classifies from the 12 monthly normals you supply. Garbage or non-representative normals in → wrong class out; it has no way to know your inputs are unusual.
- Southern/northern is derived purely from the sign of
lat(or thehemispherestring). Equatorial locations (lat≈ 0) are treated as northern; for tropicalAclimates the hemisphere choice rarely changes the code. - The four-letter
dsub-code and some rare combinations are emitted but not every one is covered by a named-city fixture in the test suite.
Tests
One command, no framework, no dependencies:
node test/koppen.test.jsIt checks known-city fixtures across every top-level group — London Cfb,
Singapore Af, Cairo BWh, Moscow Dfb, Cape Town Csb (southern
hemisphere), Utqiagvik ET (polar) — plus a hemisphere-flip case proving the
lat/hemisphere argument changes the result, the return-shape contract, and
input validation. Exit code is non-zero on any failure.
Status
Published to npm as @verifyhash/koppen-classifier;
source lives in the verifyhash/libs monorepo.
Version bumps and republishing remain a human-approved step.
License
MIT.
Install
npm install @verifyhash/koppen-classifierconst { classify } = require('@verifyhash/koppen-classifier');
const london = classify({
tempsC: [5.2, 5.3, 7.6, 9.6, 12.9, 16.0, 18.1, 17.8, 15.2, 11.4, 7.8, 5.5],
precipMm: [55, 41, 42, 44, 49, 45, 45, 50, 49, 69, 59, 55],
lat: 51.5,
});
console.log(london.code); // 'Cfb'