aboutsummaryrefslogtreecommitdiff
path: root/internal/render/scale.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/render/scale.go')
-rw-r--r--internal/render/scale.go92
1 files changed, 92 insertions, 0 deletions
diff --git a/internal/render/scale.go b/internal/render/scale.go
new file mode 100644
index 0000000..a2eb2dc
--- /dev/null
+++ b/internal/render/scale.go
@@ -0,0 +1,92 @@
+package render
+
+import "math"
+
+// Temperature bands. The two ends are IMGW's own warning criteria, so a red
+// temperature means the met office would issue a warning about it rather than
+// that it looked hot to whoever wrote this:
+//
+// Tmin <= -15 silny mroz, stopien 1 -> bright blue
+// Tmax >= 30 upal, stopien 1 -> red
+// Tmax > 35 the higher heat level -> bright red
+//
+// The splits between (0, 10, 20) are round numbers, not thresholds from any
+// source; they only subdivide the range nobody warns about.
+var tempBands = []struct {
+ below float64
+ code string
+}{
+ {0, Blue}, {10, Cyan}, {20, Green}, {30, Yellow},
+}
+
+// Deg rounds to whole degrees. Rounding through int conversion avoids "-0",
+// which is arithmetically fine and visually wrong.
+func Deg(t float64) int {
+ return int(math.Round(t))
+}
+
+// TempStyle is the ANSI code for a temperature in Celsius.
+//
+// The warning edges are written out rather than folded into tempBands so they
+// match IMGW's criteria exactly, inclusive and exclusive included.
+//
+// The value is rounded first, so the colour always matches the number printed
+// beside it: otherwise -14.6 prints as "-15" in a different colour than a true
+// -15 and looks like a rendering bug.
+func TempStyle(celsius float64) string {
+ t := float64(Deg(celsius))
+ switch {
+ case t <= -15:
+ return BrightBlue
+ case t > 35:
+ return BrightRed
+ case t >= 30:
+ return Red
+ }
+ for _, b := range tempBands {
+ if t < b.below {
+ return b.code
+ }
+ }
+ return Yellow
+}
+
+// Pollen bands in grains/m3, from Polish clinical sources. Each entry is an
+// upper bound (exclusive) and a band key; a nil bound means "everything above".
+//
+// grass -- alergen.info.pl symptom table: 20 = first nasal symptoms in 25%
+// of sufferers, 50 = symptoms in all tested, 65 = intensified in
+// over 75%, 120 = dyspnoea after 30 minutes of exposure.
+// birch -- mp.pl: 80 provokes symptoms in over 95% of allergics.
+// mugwort -- mp.pl: over 70 counts as high, intensified symptoms.
+//
+// Birch and mugwort have a single published anchor each, so they get a two-way
+// split rather than four bands: their "low" is weaker evidence than grass's.
+// Alder, olive and ragweed have no Polish threshold that could be sourced and
+// are deliberately left unbanded rather than banded on a guess.
+var pollenBands = map[string][]struct {
+ below float64
+ band string
+}{
+ "grass": {{20, "low"}, {50, "medium"}, {65, "high"}, {math.Inf(1), "very high"}},
+ "birch": {{80, "low"}, {math.Inf(1), "high"}},
+ "mugwort": {{70, "low"}, {math.Inf(1), "high"}},
+}
+
+// PollenBand is the qualitative level for a count, or "" where no threshold
+// exists for that species.
+func PollenBand(species string, value float64) string {
+ if value < 1 {
+ return "none"
+ }
+ bands, ok := pollenBands[species]
+ if !ok {
+ return ""
+ }
+ for _, b := range bands {
+ if value < b.below {
+ return b.band
+ }
+ }
+ return ""
+}