1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
package render
import "strings"
// asciiMap is deliberately one rune to one ASCII character.
//
// Substitution happens after the table has been laid out, so anything that
// changed the number of characters would shear every column to its right. That
// constraint is why the wind arrows become single letters rather than the
// two-letter compass points that would read better.
var asciiMap = map[rune]string{
// Polish diacritics, so a Polish forecast survives GSM-7.
'ą': "a", 'ć': "c", 'ę': "e", 'ł': "l", 'ń': "n",
'ó': "o", 'ś': "s", 'ź': "z", 'ż': "z",
'Ą': "A", 'Ć': "C", 'Ę': "E", 'Ł': "L", 'Ń': "N",
'Ó': "O", 'Ś': "S", 'Ź': "Z", 'Ż': "Z",
// Wind arrows. The arrow points the way the wind blows, so v is a northerly.
'↑': "^", '↓': "v", '←': "<", '→': ">",
'↖': "\\", '↗': "/", '↙': "/", '↘': "\\",
// Chart: blocks and the axis rule.
'█': "#", '▇': "#", '▆': "=", '▅': "=",
'▄': "_", '▃': ":", '▂': ".", '▁': ".",
'│': "|",
}
// ASCII rewrites output to pure ASCII, one character for one character.
//
// The point is SMS: a single non-ASCII character forces the whole message from
// GSM-7 into UCS-2, which cuts a segment from 160 characters to 70. A degree
// sign alone therefore more than doubles the cost of sending a forecast.
//
// The degree sign becomes the unit letter, which is both ASCII and clearer to
// someone reading it cold: "29C" rather than "29".
func ASCII(s, units string) string {
degree := "C"
if units == "imperial" {
degree = "F"
}
runes := []rune(s)
var b strings.Builder
b.Grow(len(s))
for i, r := range runes {
switch {
case r == '°':
// Prose from IMGW already writes "30°C"; appending the unit again
// would give "30CC". Only a bare degree sign gains the letter.
if i+1 < len(runes) && (runes[i+1] == 'C' || runes[i+1] == 'F') {
continue
}
b.WriteString(degree)
case r < 128:
b.WriteRune(r)
default:
if sub, ok := asciiMap[r]; ok {
b.WriteString(sub)
} else {
// Anything unmapped -- a place name in another script, a weather
// code description we have not transliterated -- becomes '?'
// rather than silently vanishing and misaligning the row.
b.WriteString("?")
}
}
}
return b.String()
}
|